I'm getting this error from what I assume is coming from my decorator:
TypeError: update_wrapper() missing 1 required positional argument: 'wrapper'
this is my decorator:
def authenticate_restful(f):
#wraps
def decorated_function(*args, **kwargs):
response_object = {
'status': 'fail',
'message': 'Provide a valid auth token.'
}
auth_header = request.headers.get('Authorization')
if not auth_header:
return jsonify(response_object), 403
auth_token = auth_header.split(' ')[1]
resp = User.decode_auth_token(auth_token)
if isinstance(resp, str):
response_object['message'] = resp
return jsonify(response_object), 401
user = User.query.filter_by(id=resp['sub']).first()
if not user or not user.active:
return jsonify(response_object), 401
return f(resp, *args, **kwargs)
return decorated_function
I'm running tests on this block of code and I have no idea how to go about debugging this.
Why it can possibly be missing the wrapper argument ?
functools.wraps expects a positional argument. Since you didn't provide it, it gives you an error. You need to do this:
def authenticate_restful(f):
#wraps(f)
...
Related
I have try-catch that behaved differently, Once an exception is raised, the next time the try-catch block is called, it's always raising an exception even if the params passed are valid
class Some
def some_method(variable_id: str):
response = requests.request('https://...........)
if response.status_code != 200:
if response.status_code == 404:
raise ClientError(
'_NOT_FOUND', 'Consignment does not exist. Please provide a valid variable_id', 404)
elif response.status_code == 400:
raise ClientError(
'_ALREADY_CANCELLED', f"Cannot print {variable_id}", 400)
raise ClientError(
'ERROR', "ERROR", 400)
return response
try catch
class Other:
def __init__(self):
self.error = False
def somefunc(id: str):
//id = '123' //working one
try:
response = Some().some_method(id)
return self.error, response
except Exception as e:
self.error = True
return error,[]
The weird thing is if I first called the try-catch using the id that the API check as valid, it will return the response in the try-catch block as expected. But when I replace the id with a value that the API is returning 400 status_code, it will always raise the 400 status code exception even if I called the try-catch block with the previous id that should return 200.
So I tried to print the response.status_code, it is correctly 200 for a valid id, and 400 for an invalid id, but I don't know why it always raises the exception after I call the try-catch block with an invalid id, and change again with a valid id instead of returning the response in Some.some_method class method.
What have I done wrong here?
Ah my bad. I need to reset the error
def somefunc(id: str):
//id = '123'
try:
response = Some().some_method(id)
return self.error, response
except Exception as e:
self.error = True
return error,[]
finally:
self.error = False
I am trying to build a decorator to verify aws cognito token.
Below is the dummy code that I tried.
Is there any improvements needed or is it correct way.
def verify_cognito_token(func):
def wrapper(*args, **kwargs):
parser.add_argument('Authorization', location='headers')
args = parser.parse_args()
token = args.get("Authorization")
print("Inside wrapper : ", token)
if token:
try:
verified_claims: dict = cognitojwt.decode(
token,
config['aws']['aws_cognito_region'],
config['aws']['aws_cognito_userpool_id'],
app_client_id = config['aws']['aws_cognito_app_client_id'] # Optional
# testmode=True # Disable token expiration check for testing purposes
)
print("Token verified")
val = func(*args, **kwargs)
return val
except CognitoJWTException as e:
print("Token expired")
return {"message":400}
else:
print("Token missing")
return {"message":401}
return wrapper
class HomePage(Resource):
#verify_cognito_token
def post(self):
parser.add_argument('Authorization', location='headers')
args = parser.parse_args()
print("Inside method: ",args)
return jsonify({"success":200})
api.add_resource(HomePage, "/index")
I have implemented for our django back-end application (SP) possibility to login via SAML, as IDP im using Keycloak. It works fine, but I want to write tests to be sure that all logic is being executed correctly. For this I want to generate a post request with SAML as body and mock (unittest.mock.patch) the real request. But i stuck.
Here is my django view, which accepts get and post requests when I try to login via SAML:
class SamlLoginView(View):
#staticmethod
def prepare_django_request(request):
if 'HTTP_X_FORWARDED_FOR' in request.META:
server_port = 443
else:
server_port = request.META.get('SERVER_PORT')
result = {
'https': 'on' if request.is_secure() else 'off',
'http_host': request.META['HTTP_HOST'],
'script_name': request.META['PATH_INFO'],
'server_port': server_port,
'get_data': request.GET.copy(),
'post_data': request.POST.copy(),
}
return result
#never_cache
def get(self, *args, **kwargs):
req = SamlLoginView.prepare_django_request(self.request)
auth = OneLogin_Saml2_Auth(req, settings.SAML_IDP_SETTINGS)
return_url = self.request.GET.get('next') or settings.LOGIN_REDIRECT_URL
return HttpResponseRedirect(auth.login(return_to=return_url))
#never_cache
def post(self, *args, **kwargs):
req = SamlLoginView.prepare_django_request(self.request)
print(req['post_data']['SAMLResponse'])
auth = OneLogin_Saml2_Auth(req, settings.SAML_IDP_SETTINGS)
auth.process_response()
errors = auth.get_errors()
if not errors:
if auth.is_authenticated():
logger.info("Login", extra={'action': 'login',
'userid': auth.get_nameid()})
user = authenticate(request=self.request,
saml_authentication=auth)
login(self.request, user)
return HttpResponseRedirect("/")
else:
raise PermissionDenied()
else:
return HttpResponseBadRequest("Error when processing SAML Response: %s" % (', '.join(errors)))
In my tests, I wanted to directly call the post method, in which there will be a saml inside:
class TestSamlLogin(TestCase):
def test_saml_auth(self, prepare):
client = APIClient()
url = reverse_lazy("miri_auth:samllogin")
saml_resp='<xml with saml response>'
resp = client.post(url, data=saml_resp)
but obviously it shows that request.POST is empty.
I then decided to make a mock for the prepare_django_request function, and manually insert the saml:
def mocked_prepare_request(request):
post_query_dict = QueryDict(mutable=True)
post_data = {
'SAMLResponse': saml_xml,
'RelayState': '/accounts/profile/'
}
post_query_dict.update(post_data)
result = {
'https': 'on',
'http_host': '<http-host>',
'script_name': '/api/auth/samllogin/',
'server_port': '443',
'get_data': {},
'post_data': post_query_dict,
}
return result
class TestSamlLogin(TestCase):
#patch('miri_auth.views.SamlLoginView.prepare_django_request', side_effect=mocked_prepare_request)
def test_saml_auth(self, prepare):
client = APIClient()
url = reverse_lazy("miri_auth:samllogin")
saml_resp='<xml with saml response>'
resp = client.post(url, data=saml_resp)
and depending on how I pass the saml_xml it throws different errors, if i define it as string:
with open(os.path.join(TEST_FILES_PATH, 'saml.xml')) as f:
saml_xml = " ".join([x.strip() for x in f])
it returns: lxml.etree.XMLSyntaxError: Start tag expected, '<' not found, line 1, column 1, although I checked the output from saml_xml in the xml validator and it says that the xml is valid.
When i try to parse the file into xml in advance, i get another error later,
libraries with which I tried to parse:
import xml.etree.ElementTree as ET
from xml.dom import minidom
from lxml import etree
tree = etree.parse(os.path.join(TEST_FILES_PATH, 'saml.xml'))
it returns:
TypeError: argument should be a bytes-like object or ASCII string, not '_ElementTree'
Debugging these errors didn't lead me to any solution.
If anyone has any thoughts on how this can be implemented (Mocking response with SAML), or where I made a mistake, I would be glad to hear.
Thank in advance
I realized that the SAML Response must be encoded:
with open(os.path.join(TEST_FILES_PATH, 'saml.xml')) as f:
saml_xml = " ".join([x.strip() for x in f])
base64_saml = base64.b64encode(saml_xml.encode('ascii')).decode('ascii')
post_data = {'SAMLResponse': base64_saml, 'RelayState': '/accounts/profile/'}
url = reverse_lazy("miri_auth:samllogin")
request = self.client.post(url, post_data)
but now i am getting the following errors:
func=xmlSecOpenSSLEvpDigestVerify:file=digests.c:line=280:obj=sha256:subj=unknown:error=12:invalid data:data and digest do not match
I'm trying to implement flask_jwt_extended to my flask app.
My Use Case is, I want to set Authorization headers to every request. So that when a #jwt_required decorator is decorated to a flask route it can be access if an access token is present on the Authorization headers.
I've tried some solutions like the one below:
I use #app.after_request to attach headers to every request but still it gives me this response
{
"msg": "Missing Authorization Header"
}
Here is my code:
#app.after_request
def add_headers(resp):
access_token = session.get("access_token", None)
if access_token is not None:
resp.headers["Authorization"] = f"Bearer {access_token}"
return resp
return resp
my login route:
#app.route('/', methods=["GET", "POST"])
def login():
if request.method == "POST":
form = request.form
_username = form["username"]
_password = form["password"]
for username in Users:
if username.get(_username):
if safe_str_cmp(username[_username]["password"],_password):
access_token = create_access_token(identity=username[_username]["user_id"], fresh = True)
session["access_token"] = access_token
res = make_response(redirect(url_for("home")))
res.headers["Authorization"] = f"Bearer {access_token}"
return res
else:
return "Incorrect password"
return f"Hello{_username} doesn't exist"
return render_template("login.html")
Here is my protected route:
#app.route('/home')
#jwt_required
def home():
res = Response(render_template("base.html"))
return res
I've also tried adding my headers in the route,But it still the headers I specify are not recognize and still give me the same response message. Here's how do it
#app.route('/home')
#jwt_required
def home():
access_token = session.get("access_token", None)
print(access_token)
if access_token is not None:
res = Response(render_template("base.html"), headers={"Authorization" : f"Bearer {access_token}"})
return res
res = Response(render_template("base.html"))
return res
Writing this for those who will come across this and are trying to implement an OAuth flow
Don't use decorators use middleware instead
I believe you should handle this in a middleware. In the middleware, you can set the authorization property of the 2nd parameter of the call function which contains the current wsgi app environment variables you have passed in your init function. Look at code below:
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
cookie = Request(environ).cookies.get('access_token')
if cookie is not None:
environ['HTTP_AUTHORIZATION']='Bearer '+cookie
return self.app(environ, start_response)
I need read 'dataset' in get function for showing amount and name in the template but I can't access to 'dataset' in get function
class Port(View):
def post(self, request, pid):
session = requests.Session()
response = session.get("http://localhost:8001/pay/" + str(pid))
if response.status_code is 200:
try:
dataset = json.loads(request.body.decode('utf-8'))
print("###",data['amount'],' - ',data['name'],' - ',pid)
return dataset
except ConnectionError as ce:
print(ce)
return HttpResponse("Json Parse Error")
return dataset
else:
return HttpResponse("* wrong request *")
def get(self, request, pid):
context = {
'amount' : dataset['amount'],
'firstname' : dataset['name'],
'order_id' : pid,
}
return render(request, 'index.html',context)
I believe dataset is initialized as an attribute in Port class. Use self.dataset