How to add expiry to JWE? - python

I am trying to add expiry time to JWE which I am generating using jwcrypto library in the following way
from jwcrypto import jwe, jwk, jwt
from datetime import datetime, timedelta
import time
# create JWK from existing key
jwk_str = '{"k":"29Js2yXM6P_4v9K1mHDlYVHw8Xvm_GEhvMTvKTRLRzY","kty":"oct"}'
jwk_key = jwk.JWK.from_json(jwk_str)
# calculate expiry time
d = datetime.now() + timedelta(seconds=5)
epoch = datetime.utcfromtimestamp(0)
total_seconds = (d - epoch).total_seconds()
# Add exp to the claims
claims={"exp": total_seconds, "sub": "Some random payload"}
print(claims)
jwttoken = jwt.JWT(header={"alg": "A256KW", "enc": "A256CBC-HS512"}, claims=claims)
jwttoken.make_encrypted_token(jwk_key)
jwetokenstr = jwttoken.serialize()
print(jwetokenstr)
# wait for 10 seconds to cross the expiry time
time.sleep(10)
jwttoken = jwt.JWT()
jwttoken.deserialize(token, jwk_key) # Ideally this line should fail as expiry is reached but it doesn't
print(jwttoken.claims)
I am getting the payload, but expiry claim is not read and doesn't fail on expiry.
What I am doing wrong ?

This ends up reducing to a datetime manipulation bug.
The exp claim of a JSON web token should filled out with the seconds from epoch of the expiration time.
datetime.now() returns a local time (not UTC time) datetime.datetime object. The code above then goes on to subtract this local time datetime.datetime object from the UTC time datetime.datetime object of 0-epoch time and evaluates the total seconds between these two to determine the expiry time. However, because this is comparing a local time datetime to a UTC time datetime, the number of seconds here is actually off of the epoch time by a constant factor of your local timezone difference from UTC.
For example, if I live in a place where the time is 5 hours earlier than UTC, I will actually use an epoch time that is 5 * 60 * 60 seconds off of the true epoch time I want for the expiry with this code.
Instead you could simply use round(time.time()) + x where x is the number of seconds forward in the future the JWT should expire. time.time() returns the seconds from epoch (but as a float so you need to round) from epoch.
For example:
from jwcrypto import jwe, jwk, jwt
from datetime import datetime, timedelta
import time
jwk_str = '{"k":"29Js2yXM6P_4v9K1mHDlYVHw8Xvm_GEhvMTvKTRLRzY","kty":"oct"}'
jwk_key = jwk.JWK.from_json(jwk_str)
jwt_valid_seconds = 3
expiry_time = round(time.time()) + jwt_valid_seconds
claims={"exp": expiry_time, "sub": "Some random payload"}
jwttoken = jwt.JWT(header={"alg": "A256KW", "enc": "A256CBC-HS512"}, claims=claims)
jwttoken.make_encrypted_token(jwk_key)
jwetokenstr = jwttoken.serialize()
jwttoken2 = jwt.JWT()
jwttoken2.deserialize(jwetokenstr, jwk_key)
print('This should succeed because we are deserializing immediately before the JWT has expired:')
print(jwttoken2.claims)
# Wait for the JWT to expire, and then extra time for the leeway.
leeway = 60
time.sleep(leeway + jwt_valid_seconds + 1)
jwttoken2 = jwt.JWT()
print('\nThis should fail due to the JWT expiring:')
jwttoken2.deserialize(jwetokenstr, jwk_key)
gives the output
(env) $ python jwe_expiry.py
This should succeed because we are deserializing immediately before the JWT has expired:
{"exp":1576737332,"sub":"Some random payload"}
This should fail due to the JWT expiring:
Traceback (most recent call last):
File "jwe_expiry.py", line 26, in <module>
jwttoken2.deserialize(jwetokenstr, jwk_key)
File "... python3.7/site-packages/jwcrypto/jwt.py", line 493, in deserialize
self._check_provided_claims()
File "... python3.7/site-packages/jwcrypto/jwt.py", line 370, in _check_provided_claims
self._check_default_claims(claims)
File "... python3.7/site-packages/jwcrypto/jwt.py", line 351, in _check_default_claims
self._check_exp(claims['exp'], time.time(), self._leeway)
File "... python3.7/site-packages/jwcrypto/jwt.py", line 333, in _check_exp
claim, limit, leeway))
jwcrypto.jwt.JWTExpired: Expired at 1576737332, time: 1576737392(leeway: 60)

Related

Python and Google Calendar API : Timezone / Time Offset issues

I'm having issues with timezones and time offsets when working with Python, the Google Calendar API, and a client device operating in local time.
I have a Raspberry Pi which acts as a booking display outside a shared room. A Python script is called periodically to pull in the next upcoming calendar event from the Google Calendar API, split the result into variables, then pass those variables back to the bash script which called it.
I built it in the winter—when the UK's timezone has equivalence to UTC—and it worked perfectly. Now, in British Summer Time, it doesn't. The comparison of time objects to check whether or not the meeting is currently in progress doesn't work, and the room is showing as available when it isn't.
I've read and read (then re-read and re-read) articles and explainers about working with timezones and time offsets in Python, and I just cannot get my head around it. It doesn't help that I don't really understand object-based programming, so the example scripts provided don't mean much to me!
Ideally, I would simply like everything to work using local time. The Raspberry Pi is connected to the internet and its time is updated automatically, and the Google Calendar is correctly set to the UK timezone. How could I go about making all references to time 'local', so the time objects everywhere in the Python script are always current UK time?
Please help. Stack Overflow has been such a rich source of knowledge in the past that I've never needed to ask a question myself, but this is making my brain hurt! Thank you!
This is what I've got, which is a modified version of Google's own example script:
service = build('calendar', 'v3', credentials=creds)
# Call the Calendar API
timeStamp_now = datetime.datetime.utcnow()
timeStamp_12 = timeStamp_now + timedelta(hours = 12)
now = timeStamp_now.isoformat() + 'Z' # 'Z' indicates UTC time
now12 = timeStamp_12.isoformat() + 'Z' # 'Z' indicates UTC time
events_result = service.events().list(calendarId='################resource.calendar.google.com', timeZone="Europe/London", timeMin=now,
maxResults=1, singleEvents=True, showDeleted=False, timeMax=now12,
orderBy='startTime').execute()
events = events_result.get('items', [])
if not events:
print('meetingStart="NoEvents"')
for event in events:
startTime=event['start'].get('dateTime')
endTime=event['end'].get('dateTime')
eventTitle=event['summary']
eventOrganizer=event['creator'].get('email')
try:
eventConferencing=event['conferenceData'].get('conferenceSolution').get('name')
except:
eventConferencing=(' ')
if startTime <= now <= endTime:
inProgress=('true')
else:
inProgress=('false')
safeEventTitle= ""
for i in eventTitle:
num = ord(i)
if (num >=0) :
if (num <=127) :
safeEventTitle= safeEventTitle + i
print('meetingStart="' + startTime + '"')
print('meetingEnd="' + endTime + '"')
print('meetingTitle="' + safeEventTitle + '"')
print('meetingOrganizer="' + eventOrganizer + '"')
print('meetingConferencing="' + eventConferencing + '"')
print('meetingInProgress="' + inProgress + '"')
The Raspberry Pi's time is set correctly:
pi#raspberrypi:~ $ date
Fri 16 Jul 20:31:38 BST 2021
The Python script returns this when run:
pi#raspberrypi:~ python /myPythonScript.sh
meetingStart="2021-07-16T20:00:00+01:00"
meetingEnd="2021-07-16T21:00:00+01:00"
meetingTitle="Test"
meetingOrganizer="abc#def.com"
meetingConferencing=" "
meetingInProgress="false"
See a sample code below that will adjust your datetime to UTC regardless of Daylight Saving Time.
Code:
import datetime
import pytz
timeZone = pytz.timezone("Europe/London")
dt = datetime.datetime.utcnow()
print("datetime now is:\t", dt)
local_dt = timeZone.localize(dt, is_dst=None)
utc_dt = local_dt.astimezone(pytz.utc)
print("Non-DST time is:\t", utc_dt) # prints utc regardless of DST
Output using bst datetime:
Output using utc datetime:
In your case:
timeZone = pytz.timezone("Europe/London")
dt = datetime.datetime.utcnow()
local_dt = timeZone.localize(dt, is_dst=None)
# timeStamp_now below should be in UTC already
timeStamp_now = local_dt.astimezone(pytz.utc)
Reference:
How to convert local time string to UTC?
Python daylight savings time

Did I correctly account for daylight savings time in this code?

So I'm pulling data from a druid database for a given time period. For example:
time_from = '2020-11-26T06:00:00'
time_to = '2020-11-29T06:00:00'
Between those times^.
However, depending on the day, the time is either MST (MOUNTAIN standard time) or MDT (MOUNTAIN daylight savings time), which determines the -06:00 or -07:00 at the end.
Is the following code a correct way for accounting for daylight savings time?
from datetime import datetime
import pytz #must run 'pip install pytz'
#ENTER TIMES HERE FOR MAIN.PY
time_from_1='2020-11-26T06:00:00'
time_to_1='2020-11-29T06:00:00'
#CHECKING MST/MDT AND UPDATING TIMES IF NECESSARY
naive = datetime.strptime(time_from_1, '%Y-%m-%dT%H:%M:%S')
mountain = pytz.timezone('America/Denver')
time_from_aware = mountain.localize(naive, is_dst=None)
naive = datetime.strptime(time_to_1, '%Y-%m-%dT%H:%M:%S')
mountain = pytz.timezone('America/Denver')
time_to_aware = mountain.localize(naive, is_dst=None)
def is_dst(aware_dt):
assert aware_dt.tzinfo is not None
assert aware_dt.tzinfo.utcoffset(aware_dt) is not None
return bool(aware_dt.dst())
if is_dst(time_from_aware)==True:
time_from=time_from_1+'-06:00'
else:
time_from=time_from_1+'-07:00'
if is_dst(time_to_aware)==True:
time_to=time_to_1+'-06:00'
else:
time_to=time_to_1+'-07:00'
print(time_from)
print(time_to)

Use the schedule library to run a job once tomorrow

schedule.wednesday.at("13:15").do(job)
With this code I can run fuction on wednesday 13:15. But I want to make action like, "run function next day morning '10:00' " How can do this?
Wrap job to return schedule.CancelJob such that it runs only once. Then schedule it to run every day at 10:00.
def job_once():
job()
return schedule.CancelJob
schedule.every().day.at('10:00').do(job_once)
Caveat: if the current time is before 10:00, then the job will run this day at 10:00. There's no way to define an initial delay in the schedule library - but we could get the name of the next day and schedule on this day.
import datetime
tomorrow = (datetime.datetime.now() + datetime.timedelta(days=1)).strftime('%A').lower()
getattr(schedule.every(), tomorrow).at('10:00').do(job_once)
Python contains a simple library called sched:
import sched, time
def functionYouWantToRun():
...
s = sched.scheduler(time.localtime, time.sleep)
s.enterabs(time.strptime('[three character day] [three character month] [day of month] [time (hh:mm:ss)] [year]')
s.run()
def job_that_executes_once():
# Do some work that only needs to happen once...
return schedule.CancelJob
if __name__ == "__main__":
from datetime import datetime, timedelta
import schedule
schedule.every().day.at("10:00").do(job_that_executes_once)
I was going to go with time offset, but then realised the above is easier. That will run it the next day.
If you need to run several days in the future, then you are back to datetime offsets:
x=datetime.today()
y = x.replace(day=x.day, hour=10, minute=0, second=0, microsecond=0) +
timedelta(days=1)
yDay = y.strftime('%A')

Run Function at Given DateTime [duplicate]

How can I run a function in Python, at a given time?
For example:
run_it_at(func, '2012-07-17 15:50:00')
and it will run the function func at 2012-07-17 15:50:00.
I tried the sched.scheduler, but it didn't start my function.
import time as time_module
scheduler = sched.scheduler(time_module.time, time_module.sleep)
t = time_module.strptime('2012-07-17 15:50:00', '%Y-%m-%d %H:%M:%S')
t = time_module.mktime(t)
scheduler_e = scheduler.enterabs(t, 1, self.update, ())
What can I do?
Reading the docs from http://docs.python.org/py3k/library/sched.html:
Going from that we need to work out a delay (in seconds)...
from datetime import datetime
now = datetime.now()
Then use datetime.strptime to parse '2012-07-17 15:50:00' (I'll leave the format string to you)
# I'm just creating a datetime in 3 hours... (you'd use output from above)
from datetime import timedelta
run_at = now + timedelta(hours=3)
delay = (run_at - now).total_seconds()
You can then use delay to pass into a threading.Timer instance, eg:
threading.Timer(delay, self.update).start()
Take a look at the Advanced Python Scheduler, APScheduler: http://packages.python.org/APScheduler/index.html
They have an example for just this usecase:
http://packages.python.org/APScheduler/dateschedule.html
from datetime import date
from apscheduler.scheduler import Scheduler
# Start the scheduler
sched = Scheduler()
sched.start()
# Define the function that is to be executed
def my_job(text):
print text
# The job will be executed on November 6th, 2009
exec_date = date(2009, 11, 6)
# Store the job in a variable in case we want to cancel it
job = sched.add_date_job(my_job, exec_date, ['text'])
Might be worth installing this library: https://pypi.python.org/pypi/schedule, basically helps do everything you just described. Here's an example:
import schedule
import time
def job():
print("I'm working...")
schedule.every(10).minutes.do(job)
schedule.every().hour.do(job)
schedule.every().day.at("10:30").do(job)
schedule.every().monday.do(job)
schedule.every().wednesday.at("13:15").do(job)
while True:
schedule.run_pending()
time.sleep(1)
Here's an update to stephenbez' answer for version 3.5 of APScheduler using Python 2.7:
import os, time
from apscheduler.schedulers.background import BackgroundScheduler
from datetime import datetime, timedelta
def tick(text):
print(text + '! The time is: %s' % datetime.now())
scheduler = BackgroundScheduler()
dd = datetime.now() + timedelta(seconds=3)
scheduler.add_job(tick, 'date',run_date=dd, args=['TICK'])
dd = datetime.now() + timedelta(seconds=6)
scheduler.add_job(tick, 'date',run_date=dd, kwargs={'text':'TOCK'})
scheduler.start()
print('Press Ctrl+{0} to exit'.format('Break' if os.name == 'nt' else 'C'))
try:
# This is here to simulate application activity (which keeps the main thread alive).
while True:
time.sleep(2)
except (KeyboardInterrupt, SystemExit):
# Not strictly necessary if daemonic mode is enabled but should be done if possible
scheduler.shutdown()
I've confirmed the code in the opening post works, just lacking scheduler.run(). Tested and it runs the scheduled event. So that is another valid answer.
>>> import sched
>>> import time as time_module
>>> def myfunc(): print("Working")
...
>>> scheduler = sched.scheduler(time_module.time, time_module.sleep)
>>> t = time_module.strptime('2020-01-11 13:36:00', '%Y-%m-%d %H:%M:%S')
>>> t = time_module.mktime(t)
>>> scheduler_e = scheduler.enterabs(t, 1, myfunc, ())
>>> scheduler.run()
Working
>>>
I ran into the same issue: I could not get absolute time events registered with sched.enterabs to be recognized by sched.run. sched.enter worked for me if I calculated a delay, but is awkward to use since I want jobs to run at specific times of day in particular time zones.
In my case, I found that the issue was that the default timefunc in the sched.scheduler initializer is not time.time (as in the example), but rather is time.monotonic. time.monotonic does not make any sense for "absolute" time schedules as, from the docs, "The reference point of the returned value is undefined, so that only the difference between the results of consecutive calls is valid."
The solution for me was to initialize the scheduler as
scheduler = sched.scheduler(time.time, time.sleep)
It is unclear whether your time_module.time is actually time.time or time.monotonic, but it works fine when I initialize it properly.
dateSTR = datetime.datetime.now().strftime("%H:%M:%S" )
if dateSTR == ("20:32:10"):
#do function
print(dateSTR)
else:
# do something useful till this time
time.sleep(1)
pass
Just looking for a Time of Day / Date event trigger:
as long as the date "string" is tied to an updated "time" string, it works as a simple TOD function. You can extend the string out to a date and time.
whether its lexicographical ordering or chronological order comparison,
as long as the string represents a point in time, the string will too.
someone kindly offered this link:
String Comparison Technique Used by Python
had a really hard time getting these answers to work how i needed it to,
but i got this working and its accurate to .01 seconds
from apscheduler.schedulers.background import BackgroundScheduler
sched = BackgroundScheduler()
sched.start()
def myjob():
print('job 1 done at: ' + str(dt.now())[:-3])
dt = datetime.datetime
Future = dt.now() + datetime.timedelta(milliseconds=2000)
job = sched.add_job(myjob, 'date', run_date=Future)
tested accuracy of timing with this code:
at first i did 2 second and 5 second delay, but wanted to test it with a more accurate measurement so i tried again with 2.55 second delay and 5.55 second delay
dt = datetime.datetime
Future = dt.now() + datetime.timedelta(milliseconds=2550)
Future2 = dt.now() + datetime.timedelta(milliseconds=5550)
def myjob1():
print('job 1 done at: ' + str(dt.now())[:-3])
def myjob2():
print('job 2 done at: ' + str(dt.now())[:-3])
print(' current time: ' + str(dt.now())[:-3])
print(' do job 1 at: ' + str(Future)[:-3] + '''
do job 2 at: ''' + str(Future2)[:-3])
job = sched.add_job(myjob1, 'date', run_date=Future)
job2 = sched.add_job(myjob2, 'date', run_date=Future2)
and got these results:
current time: 2020-12-10 19:50:44.632
do job 1 at: 2020-12-10 19:50:47.182
do job 2 at: 2020-12-10 19:50:50.182
job 1 done at: 2020-12-10 19:50:47.184
job 2 done at: 2020-12-10 19:50:50.183
accurate to .002 of a second with 1 test
but i did run a lot of tests and accuracy ranged from .002 to .011
never going under the 2.55 or 5.55 second delay
#everytime you print action_now it will check your current time and tell you should be done
import datetime
current_time = datetime.datetime.now()
current_time.hour
schedule = {
'8':'prep',
'9':'Note review',
'10':'code',
'11':'15 min teabreak ',
'12':'code',
'13':'Lunch Break',
'14':'Test',
'15':'Talk',
'16':'30 min for code ',
'17':'Free',
'18':'Help ',
'19':'watever',
'20':'watever',
'21':'watever',
'22':'watever'
}
action_now = schedule[str(current_time.hour)]

Adding user time in python 3

Hi Everyone i have googles to my hearts content but have not found the answer.
Basically I want to add user inputted time to the current time.
This is just a small project I'm working on while learning Python.
So if the current time is 17:16 and the user wants to add 1hr 30 to that. how would i do it.
This is what i have:
import datetime
flex = input("Enter your flex amount in HHMM:")
flex = flex[0]+flex[1]+"-"+flex[2]+flex[3]
time = datetime.datetime.now().strftime("%H-%M")
balance = time+flex
print(time)
print(flex)
print(balance)
I have now tried
import datetime
flex = input("Enter your flex amount in HHMM:")
time = datetime.datetime.now().strftime("%H-%M")
flex = flex[0]+flex[1]+"-"+flex[2]+flex[3]
time = time[0]+time[1]+"-"+time[2]+time[3]
balance = datetime.timedelta(hours=int(time[0]+time[1]),
minutes=int(time[2]+time[3]) +
datetime.timedelta(hours=int(flex[0]+flex[1]),
minutes=int(flex[2]+flex[3]))
But now its complaining about its expecting an integer. but if i change it ot an integer will that not defeat the purpose of me wanting to add is as time.
Thanks
I got it to work using the answer. This is what it looks like now thanks pal.
from datetime import timedelta as td
import datetime as da
#flex = input("Enter your flex amount in HHMM:")
flex = "0134"
now = da.datetime.now()
user_hours = int(flex[:2])
user_minute = int(flex[2:5])
delay = td(hours=user_hours, minutes=user_minute)
balance = da.datetime.now()+delay
print("Lunch: " +str(lunch))
print("Time when balance at 00:00 : " +str(balance))
print("Now: " +str(now))
Simple using timedelta create an offset indicated by timedelta object and ad it to your time object (working the same with date and datetime too).
from datetime import timedelta, datetime
actual_time = datetime.now()
user_hours = int(flex[:3])
user_minute = int(flex[2:5])
delay = timedelta(hours=user_hours, minutes=user_minute)
print(datetime.now()+delay)
So if the current time is 17:16 and the user wants to add 1hr 30 to
that. how would i do it.
You can use timedelta, i.e.:
new_time = datetime.datetime.now() + datetime.timedelta(hours=1, minutes=30) # or simply minutes=90, etc...
Cool so when tring
balance = datetime.timedelta(hours=int(time[0]+time[1]), minutes=int(time[2]+time[3]) + datetime.timedelta(hours=int(flex[0]+flex[1]), minutes=int(flex[2]+flex[3]))
its complaining that its expecting an interger not a time delta

Categories