python - Flask basic auth testing with pytest - python

I'm currently using Flask-HTTPAuth for basic authentication within my project. I've tested it by hand using curl and base64 tokens and it does indeed work. However I'm running into problems creating tests proving it works. This is my current test and it always turns back 401:
class TestLoginApi
def setup(self):
myapp.app.config.from_object("config.TestingConfig")
self.app = myapp.app.test_client()
client = MongoClient(myapp.app.config['DATABASE_URL'])
db = client.get_default_database()
assert list(db.collection_names()) == []
db.users.insert_one({'name': 'testuser', 'password': 'testpassword'})
def teardown(self):
client = MongoClient(myapp.app.config['DATABASE_URL'])
client.drop_database(client.get_default_database())
def test_not_logged_in(self):
rv = self.app.get('/api/v1/login/')
assert rv.status_code == 401
def test_correct_password(self):
d = Headers()
d.add('Authorization', 'Basic dGVzdHVzZXI6dGVzdHBhc3N3b3Jk')
rv = self.app.get('/api/v1/login/', headers=d,
content_type='application/json')
assert rv.status_code == 200
I've also tried changing the following:
def test_correct_password(self):
d = Headers()
d.add('Authorization', 'Basic dGVzdHVzZXI6dGVzdHBhc3N3b3Jk')
rv = self.app.get('/api/v1/login/', headers=d,
content_type='application/json')
assert rv.status_code == 200
to this with no success:
def test_correct_password(self):
rv = self.app.get('/api/v1/login/', headers={"Authorization": "Basic {user}".format(user=base64.b64encode(b"testuser:testpassword"))})
assert rv.status_code == 200

You can test authorization like this:
import base64
valid_credentials = base64.b64encode(b"testuser:testpassword").decode("utf-8")
response = self.app.get(
"/api/v1/login/",
headers={"Authorization": "Basic " + valid_credentials}
)
assert response.status_code == 200
I think you would enjoy my github project, which has a tons of different tests for Flask-HTTPAuth.

Related

Having issues with sending JWT via AXIOS ..normal routes work however AJAX post says no JWT available

So I have this endpoint below(Block 2) called from the directly below(Block 1)...the headers I see in the browser appear to be sending the access_token_cookie in the cookie header....I also have a after_header block(Block 3)
THE AXIOS CALL FROM THE PAGE
var myFunction = function(element){
console.log(element.innerText);
axios.defaults.withCredentials = true;
axios.post('/api/cmsaudit/'+element.innerText).then(function(response){
//console.log(response.data);
document.getElementById('resultcol').innerHTML = response.data;
}).catch(function(error){
})
THE AJAX ENDPOINT CALL
#cms_blueprint.route('/api/cmsaudit/<cause>',methods=['POST','GET'])
#jwt_required() <---- if I remove this it works fine.
def applist(cause):
f = open('cause.txt','w')
f.write('inhere')
f.write(str(datetime.now()))
getapps = OracleConnect()
opencon = getapps.openconnection()
if opencon == 0:
if cause == 'List Applications':
getapps.mycursor.execute("QUERY")
result = getapps.mycursor.fetchall()
getapps.closeconnection
print(result)
results = {}
results["value"]=[]
for x in result:
results["value"].append(x[0])
sys.stdout.flush()
return render_template('cmsaudit/applist.html',applist=results["value"])
return jsonify(results),200
else:
f.write('else is running')
return jsonify({'msg':str(opencon)})
AFTER REQUEST BLOCK TO CHECK/REFRESH TOKEN
#app.after_request
def refresh_expiring_jwts(response):
try:
now = datetime.now()
if request.path == '/api/jwt/check':
return response
exp_timestamp = get_jwt()["exp"]
target_timestamp = datetime.timestamp(now + timedelta(minutes=28))
if target_timestamp > exp_timestamp:
access_token = create_access_token(identity=get_jwt_identity())
set_access_cookies(response, access_token)
return response
except (RuntimeError, KeyError):
# Case where there is not a valid JWT. Just return the original respone
return response

How to mock url with Pytest parametrize and responses

I am new to unit testing in Python. I am trying to mock a response, but the url doesn't get mocked returning error that the mock is not registered and gives me a hint to use the real URL, with the real one it works, but it needs to be mocked somehow. Tried out pytest parametrize without success.
This is what I tried so far:
FAKE_HOST = "https://fake-host.com"
#pytest.mark.parametrize(
("fake_url"),
[(FAKE_HOST, "https://fake-host.com")],
)
#responses.activate
def test_item(fake_url):
responses.add(
responses.GET,
f"{fake_url}/rest/info?name=item",
status=200,
)
resp = requests.get(
"https://{fake_url}/rest/info?name=item"
)
assert resp.status_code == 200
import requests
def example2():
r = requests.get("http://httpbin.org/" + "get")
if r.status_code == 200:
response_data = r.json()
return r.status_code, response_data["url"]
else:
return r.status_code, ""
def test_get_response_success(monkeypatch):
class MockResponse(object):
def __init__(self):
self.status_code = 200
self.url = "http://httpbin.org/get"
self.headers = {"foobar": "foooooo"}
def json(self):
return {"fooaccount": "foo123", "url": "https://fake-host.com"}
def mock_get(url):
return MockResponse()
monkeypatch.setattr(requests, "get", mock_get)
assert example2() == (200, "https://fake-host.com")
Have you considered using monkepyatching?

Why Flask pytest wont post data within app?

I'm trying to test the following app:
import os
import json
import pytest
from flask import Flask, request, jsonify
def create_app(test_config=None):
app = Flask(__name__, instance_relative_config=True)
if test_config is None:
app.config.from_pyfile('config.py', silent=True)
else:
app.config['TESTING'] = True
try:
os.makedirs(app.instance_path)
except OSError:
pass
#app.route('/predict', methods=['POST'])
def predict():
p = (request.get_json()) * 2
try:
return jsonify({'status': 200, 'message': 'ok', 'data': p})
except Exception as e:
return jsonify({'status': 400, 'message': str(e)}), 400
return app
#pytest.fixture
def client():
app = create_app()
app.config["TESTING"] = True
app.testing = True
with app.test_client() as client:
yield client
def test_predict(client):
response = client.post('/predict', data=json.dumps({'res': 3}))
data = json.loads(response.get_data(as_text=True))
assert response.status_code == 200
assert data['data'] == 6
When I'm running the following command:
pytest
Im getting the following error:
FAILED test_flask_testing.py::test_predict - TypeError: unsupported operand type(s) for *: 'NoneType' and 'int'
It seems like my post request won't pass data to the app, what's wrong with my post request?
Thanks in advance!
The data is being passed, but I don't understand what you are trying to do with this line:
p = (request.get_json()) * 2
Did two small changes, so the tests now are passing:
## -20,7 +20,7 ## def create_app(test_config=None):
#app.route('/predict', methods=['POST'])
def predict():
- p = (request.get_json()) * 2
+ p = request.get_data()
try:
return jsonify({'status': 200, 'message': 'ok', 'data': p})
except Exception as e:
## -42,4 +42,4 ## def test_predict(client):
response = client.post('/predict', data=json.dumps({'res': 3}))
data = json.loads(response.get_data(as_text=True))
assert response.status_code == 200
- assert data['data'] == 6
+ assert data['data'] == '{"res": 3}'
If you are trying to receive the incoming data as json, then you need to run the post like this:
response = client.post('/predict', json={'res': 3})
Then you need to parse as json:
#app.route('/predict', methods=['POST'])
def predict():
p = request.get_json()

Why database datas are being deleted after restarting flask server?

Recently, I have started flask. I found flask-sqlalchemy to work with flask. So I was using this. But I faced some problems. I am working on repl.it. When the repl goes assleep, I tried to use with the flask enabled website. But saw that no datas were showing. But before that I added a lot of datas. I can't figure out what is wrong but could you help me?
It may help you:
from flask_sqlalchemy import SQLAlchemy
import re
from flask import Flask, abort, jsonify, redirect, request
import os
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite3'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
class apikey(db.Model):
apikey = db.Column(db.String(25), primary_key=True, unique = True)
def __init__(self, apikey):
self.apikey = apikey
class shorturl(db.Model):
short_query = db.Column(db.String(15), primary_key=True, unique = True)
original = db.Column(db.String(1000))
visits = db.Column(db.Integer)
def __init__(self, short_query,original, visits):
self.short_query = short_query
self.original = original
self.visits = visits
def url_valid(url):
return re.match(regex, url) is not None
def bad_request(message):
response = jsonify({'message': message})
response.status_code = 400
return response
def errreq(message):
response = jsonify({'message': message})
response.status_code = 404
return response
#app.route('/')
def show_all():
return redirect("https://www.cburst.ml", code=301)
#app.route('/addapi', methods=['GET'])
def addapi():
if request.args.get('apikey') is not None:
api_key = request.args.get('apikey')
apiadd = apikey(apikey=api_key)
db.session.add(apiadd)
db.session.commit()
return jsonify({'message':"Done"}), 200
else:
return bad_request("Nothing Provided")
#app.route('/add', methods=['POST'])
def add():
if not request.json:
return bad_request('Url must be provided in json format.')
if "original" not in request.json :
return bad_request('Url parameter not found.')
if "short_query" not in request.json:
return bad_request('Url parameter not found.')
original = request.json['original']
short = request.json['short_query']
if shorturl.query.filter_by(short_query = short).first() is not None:
return bad_request("Already Exists")
visits = 0
if original[:4] != 'http':
original = 'http://' + original
if not url_valid(original):
return bad_request('Provided url is not valid.')
url_db = shorturl(
short_query=short, original=original, visits=visits)
shortened_url = short
db.session.add(url_db)
db.session.commit()
return jsonify({'link': shortened_url}), 201
#app.route('/add', methods=['GET'])
def add_get():
if request.args.get('apikey') is not None and request.args.get('original') is not None and request.args.get('short_query') is not None:
api_key = request.args.get('apikey')
original = request.args.get('original')
short_query = request.args.get('short_query')
if apikey.query.filter_by(apikey = api_key).first() is None:
return errreq("Invalid API Key")
else:
if shorturl.query.filter_by(short_query=short_query).first() is not None:
return errreq("Already Exists")
else:
if original[:4] != 'http':
original = 'http://' + original
if not url_valid(original):
return bad_request('Provided url is not valid.')
url_db = shorturl(
short_query=short_query, original=original, visits=0)
db.session.add(url_db)
db.session.commit()
return jsonify({'link': short_query}), 200
else:
return bad_request("Nothing Provided")
#app.route('/view/<alias>', methods=['GET'])
def get_viewcount(alias):
if shorturl.query.filter_by(short_query=alias).first() is None:
return bad_request('Unknown alias.')
else:
return jsonify({'visits':shorturl.query.filter_by(short_query=alias).first().visits}),200
#app.route('/<alias>', methods=['GET'])
def get_shortened(alias):
if shorturl.query.filter_by(short_query=alias).first() is None:
return bad_request('Unknown alias.')
visits = shorturl.query.filter_by(short_query=alias).first().visits
url_db = shorturl.query.filter_by(short_query=alias).first()
url_db.visits = visits + 1
db.session.commit()
url = shorturl.query.filter_by(short_query=alias).first().original
return redirect(url, code=302)
# From https://stackoverflow.com/questions/7160737/python-how-to-validate-a-url-in-python-malformed-or-not#7160778
# Slightly modified to not use ftp.
regex = re.compile(
r'^(?:http)s?://'
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|'
r'localhost|'
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
r'(?::\d+)?'
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
if __name__ == '__main__':
'''
if os.path.exists('./db.sqlite3'):
app.run(host="0.0.0.0", port=8000, debug=False)
else: '''
db.create_all()
app.run(host="0.0.0.0", port=8000, debug=False)
Here if I add api key in /appapi with key in query string, I can add the key in the database. But the key added in the database does not work after a couple of hours.
Thanks in advance.
I figured this out. It is because I have added db.create_all() inside the main function. Removing this just works.

Parse Header in Post Call Python

I am using rest client in my mozilla browser to call an auth service.
When i pass my credentials in Body, i get an "auth-token" . I then set this token in the header in the browser HEADERS tab.
I have to parse this header which i am setting in the browser in my python script as a variable. Further, after getting this value in my script i have to authenticate the token for its validity.
However i am unable to get the tokens value in my script. My auth function is ready. I just have to fetch the token
How should i fetch this token value from the header ??
Code:
def check_authentication(auth):
print "Auth" , auth
chek_auth_url = ("http://10.168.2.161/auth/v/%s" % (auth))
auth = requests.get(chek_auth_url)
if auth.status_code == 200:
return True
I have to pass the token as a paramter in this function and call in this function in main for authentication.
def crossdomain(origin=None, methods=None, headers=None, max_age=21600, attach_to_all=True, automatic_options=True):
if methods is not None:
methods = ', '.join(sorted(x.upper() for x in methods))
if headers is not None and not isinstance(headers, basestring):
headers = ', '.join(x.upper() for x in headers)
if not isinstance(origin, basestring):
origin = ', '.join(origin)
if isinstance(max_age, timedelta):
max_age = max_age.total_seconds()
def get_methods():
if methods is not None:
return methods
options_resp = current_app.make_default_options_response()
return options_resp.headers['allow']
def decorator(f):
def wrapped_function(*args, **kwargs):
if automatic_options and request.method == 'OPTIONS':
resp = current_app.make_default_options_response()
else:
resp = make_response(f(*args, **kwargs))
if not attach_to_all and request.method != 'OPTIONS':
return resp
h = resp.headers
h['Access-Control-Allow-Origin'] = origin
h['Access-Control-Allow-Methods'] = get_methods()
h['Access-Control-Max-Age'] = str(max_age)
if headers is not None:
h['Access-Control-Allow-Headers'] = headers
#h['Access-Control-Allow-Headers'] = "Content-Type"
return resp
f.provide_automatic_options = False
return update_wrapper(wrapped_function, f)
return decorator
#app.route('/test', methods=['POST', 'OPTIONS'])
#crossdomain(origin='*', headers='Content-Type')
def get_storage():
*check_authentication is called here and token is passed as a parameter*
*if token is valid further task i hav to do*
if __name__ == '__main__':
app.run(host='192.168.56.1', port=8080, threaded=True)
Self-Help is the best help..
Finally i found a fix:
The token value is fetched in the variable tokenValue. I can now do my further coding.
tokenValue = request.headers.get("token")
if tokenValue == None:
return "x-auth-token not passed in header, please pass the token."
else:
print "Token passed is", tokenValue

Categories

Resources