I'm using pyjwt as follows:
def encode_auth_token(self, user_id):
'''Generates the auth token.'''
try:
payload = {
'exp': datetime.utcnow() + datetime.timedelta(
days = current_app.config.get('TOKEN_EXPIRATION_DAYS'),
seconds = current_app.config.get('TOKEN_EXPIRATION_SECONDS')
),
'iat': datetime.datetime.utcnow(),
'sub': user_id
}
return jwt.encode(
payload,
current_app.config.get('SECRET_KEY'),
algorithm='HS256'
)
except Exception as e:
return e
the problem with this is that according to docs instance.encode() should return bytes and according to another resource it should return str. When I run it through unitttests:
auth_token = user.encode_auth_token(user.id)
self.assertTrue(isinstance(auth_token, str))
I get: AssertionError: False is not true and when I replace str with bytes I get the same error. So what type this method should be returning ?
its prolly returning byte data. If you can confirm that it does, you can force it return string by calling the decode method on the token instance itself.
token = jwt.encode(payload,secret).decode('utf-8')
return token
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
Okay so this is my code:
#client.command(aliases=['d'], pass_context=True)
async def decrypt(ctx, arg, member=discord.Member):
key = b'xxxxxx'
f = Fernet(key)
decrypted = f.decrypt(arg)
channel = await member.create_dm()
await channel.send(f'Decrypted message: {decrypted}')
I insert a string after ctx, and it says TypeError: token must be bytes.
My arg is this (which is a byte, right?): b'gAAAAABgx22pwwjUHUA7KqV8jmZrXvocfC3VrHS_QrGCfCaEyj6f7cG1_K3NtbkADYiR4l8fq-DiqYJJk2k8n0jBUhDYsH2kNA=='
First of all, pass_context is deprecated. Second, no need to use create_dm; members are messageables, so you can do member.send. Third, discord.py interprets all arguments as strings by default. You'll need to use typehints (speaking of which, = is used to assign default values, not argument types). And fourth, this will send Decrypted message: b'meetmeatthepetstore', not Decrypted message: meetmeatthepetstore, so you'll want to decode the result. Here's the result:
#client.command(aliases=['d'])
async def decrypt(ctx, arg: bytes, member: discord.Member):
key = b'mycoolkey'
f = Fernet(key)
decrypted = f.decrypt(arg).decode('utf_8')
await member.send(f'Decrypted message: {decrypted}')
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 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)
...
How can I use this function:
def generate_sig(endpoint, params, secret):
sig = endpoint
for key in sorted(params.keys()):
sig += '|%s=%s' % (key, params[key])
return hmac.new(secret, sig, sha256).hexdigest()
with these parameters:
endpoint = '/media/657988443280050001_25025320'
params = {
'access_token': 'fb2e77d.47a0479900504cb3ab4a1f626d174d2d',
'count': 10,
}
secret = '6dc1787668c64c939929c17683d7cb74'
In Python 3? Now I receive this error:
TypeError: key: expected bytes or bytearray, but got 'str'
This work for me:
def generate_sig(endpoint, params, secret):
sig = endpoint
for key in sorted(params.keys()):
sig += '|%s=%s' % (key, params[key])
return hmac.new(bytes(secret,encoding='ascii'),
bytes(sig,encoding='ascii'),
sha256).hexdigest()
This way you encode like bytes only to get the hash.