async def update(client: str, serial_number: str, background_tasks: BackgroundTasks, db: Session = Depends(get_db)): ''' Client notifies server of updated user data ''' logger.debug('Client (' + client + ') notified server that ID (' + serial_number + ') has updated') db_pass = crud.get_pass(db, serial_number) if db_pass: # if a pass exists for user, # start background pass update task background_tasks.add_task(utils.update_pass, db, serial_number) response = Response(status_code=200) logger.info('Pass (' + serial_number + ') updated by client (' + client + ') request') else: # no matching pass found, # returns HTML status no matching data response = Response(status_code=204) logger.debug('Client (' + client + ') notified server about update for a non-existing user') return response
def reader_post(request: Request, idNum: str = Form(...), db: Session = Depends(get_db)): ''' Returns user data on form submit ''' serial_number = idNum user = crud.get_pass(db, serial_number) response = templates.TemplateResponse('reader.html', \ {'request': request, 'name': user.name, 'id_num': user.serial_number, 'photo_URL': user.photo_URL}) return response
def send_passes(request: Request, pass_type: str, serial_number: str, db: Session = Depends(get_db)): ''' Getting the Latest Version of a Pass ''' auth_token = str(request.headers.get('Authorization')).replace( 'ApplePass ', '') if_modified_since = request.headers.get('if-modified-since') logger.debug('Device asked for latest version of pass (' + serial_number + ') with authorization (' + auth_token + ')') db_pass = crud.get_pass(db, serial_number, auth_token) if db_pass: # if pass exists and auth_token matches if if_modified_since: # if if_modified_since tag exists, convert to datetime obj to compare if_modified_since = datetime.strptime(if_modified_since, '%a, %d %b %Y %H:%M:%S %Z') # if the pass was updated after if_modified_since, # send the new version of the pass as a response if db_pass.last_update > if_modified_since: # force update on manual database change -> # crud.force_pass_update(db, db_pass.serial_number) response = utils.get_pass_file(db, db_pass.serial_number) logger.debug('Updated pass (' + serial_number + ') returned to device.') else: # if the pass has not changed, return HTTP status code 304 response = Response(status_code=304) logger.debug('Pass (' + serial_number + ') has not changed.') else: # if device asks for pass unconditionally, # respond with the current pass file response = utils.get_pass_file(db, db_pass.serial_number) logger.debug('Pass (' + serial_number + ') returned unconditionally') else: # if the request is not authorized, returns HTTP status 401 response = Response(status_code=401) logger.debug('Pass (' + serial_number + ') not authorized (' + auth_token + ')') return response
def delete(request: Request, device_id: str, pass_type: str, serial_number: str, db: Session = Depends(get_db)): ''' Unregistering a Device ''' auth_token = str(request.headers.get('Authorization')).replace( 'ApplePass ', '') logger.debug('Device (' + device_id + ') deleted pass (' + serial_number + ') with authorization (' + auth_token + ')') db_pass = crud.get_pass(db, serial_number, auth_token) if db_pass: # if pass exists and auth_token matches, # delete device registration for pass crud.delete_registration(db, device_id, serial_number) if not crud.get_registrations_by_device(db, device_id): # if not more passes exist for device, # delete device and push_token from device table crud.delete_device(db, device_id) # if disassociation succeeds, returns HTTP status 200 response = Response(status_code=200) logger.info('Pass (' + serial_number + ') deleted from device (' + device_id + ') ') else: if config.DEBUG: # if in debug mode, force delete response = Response(status_code=200) logger.debug('Delete request from device (' + device_id + ') not authorized (' + auth_token + ') but allowed (DEBUG)') else: # if the request is not authorized, returns HTTP status 401 response = Response(status_code=401) logger.debug('Delete request from device (' + device_id + ') not authorized (' + auth_token + ')') return response
def register(request: Request, body: dict, device_id: str, pass_type: str, serial_number: str, db: Session = Depends(get_db)): ''' Registering a Device to Receive Push Notifications for a Pass ''' auth_token = str(request.headers.get('Authorization')).replace( 'ApplePass ', '') push_token = str(body['pushToken']) logger.debug('Pass registration request from device (' + device_id + ')') if crud.get_pass(db, serial_number, auth_token): # if pass exists in database with same serial number & matching auth_token if not crud.get_device(db, device_id): # if no device with same device_id exists crud.add_device(db, device_id, push_token) if not crud.get_registration(db, device_id, serial_number): # if the device is not already registered # for a pass with same serial_number crud.add_registration(db, device_id, serial_number) # if registration succeeds, returns HTTP status 201. response = Response(status_code=201) logger.info('New user registered (' + serial_number + ')') else: # if the serial number is already registered for # this device, returns HTTP status 200. response = Response(status_code=200) logger.debug('Existing user registered (' + serial_number + ')') else: # if the request is not authorized, returns HTTP status 401 response = Response(status_code=401) logger.debug('Pass registration request from device (' + device_id + ') not authorized (' + auth_token + ')') return response
def __init__(self, db: Session, serial_number: str): # parse User data into reusable variables user_pass = crud.get_pass(db, serial_number) # Add user photo with different device resolution support response = requests.get(user_pass.photo_URL) img = Image.open(BytesIO(response.content)) img = img.resize((113, 150), Image.ANTIALIAS) hero_image = Image.new(img.mode, (600, 200), (128, 20, 41)) hero_image.paste(img, (450, 25)) draw = ImageDraw.Draw(hero_image) font = ImageFont.truetype("include/google/Roboto-Regular.ttf", 34) draw.text((37, 84), user_pass.name, (255, 255, 255), font=font) hero_image.save('static/heroImg/' + serial_number + '.png') objectUid = str(services.VerticalType.LOYALTY).split( '.')[1] + '_OBJECT_' + str(serial_number) # check Reference API for format of "id" (https://developers.google.com/pay/passes/reference/v1/). objectId = '%s.%s' % (config.ISSUER_ID, objectUid) self.objectJwt = services.makeSkinnyJwt(services.VerticalType.LOYALTY, config.CLASS_ID, objectId, user_pass)
def __init__(self, db: Session, serial_number: str): # parse User data into reusable variables user_pass = crud.get_pass(db, serial_number) passinfo = Generic() passinfo.addPrimaryField('name', user_pass.name) passinfo.addSecondaryField('cash', '$' + str(user_pass.eagle_bucks), 'Eagle Bucks', 'You have %@ Eagle Bucks remaining.', textAlignment=Alignment.LEFT) passinfo.addSecondaryField('meals', user_pass.meals_remaining, 'Meals Remaining', 'You have %@ meal swipes remaining.', textAlignment=Alignment.CENTER) passinfo.addSecondaryField('ethos', user_pass.kudos_earned + "/" + user_pass.kudos_required, 'Kudos', 'You have %@ Kudos of your Semester Goal.', textAlignment=Alignment.RIGHT) passinfo.addBackField('pin', user_pass.id_pin, 'ID Pin') if user_pass.print_balance: passinfo.addBackField('print', user_pass.print_balance, 'Print Balance', 'Your print balance is now %@.') if user_pass.mailbox: passinfo.addBackField('boxnumber', user_pass.mailbox, 'Mailbox Number') passinfo.addBackField('info', 'Please note that Automatic Updates must be turned on (default) to use the ID.\n\n' \ + 'Report Feedback:\nhttps://forms.gle/6bAWYccfs9KsNAdP8\n\n' \ + 'Created by the MOBIL-ID Team:\nAndrew Siemer, Jacob Button, Kyla Tarpey & Zach Jones\n') if config.DEBUG: passinfo.addBackField('hash', user_pass.pass_hash, 'Pass Hash') passfile = Pass(passinfo, \ passTypeIdentifier=user_pass.pass_type , \ organizationName='Oklahoma Chrisitian University' , \ teamIdentifier=config.TEAM_IDENTIFIER) passfile.sharingProhibited = True passfile.webServiceURL = config.WEB_SERVICE_URL passfile.authenticationToken = user_pass.auth_token passfile.description = 'OC ID' passfile.associatedStoreIdentifiers = [ 306012905, ] passfile.foregroundColor = 'rgb(255, 255, 255)' passfile.backgroundColor = 'rgb(128, 20, 41)' passfile.labelColor = 'rgb(255, 255, 255)' passfile.serialNumber = user_pass.serial_number passfile.barcode = Barcode(user_pass.pass_hash, BarcodeFormat.QR, user_pass.serial_number) passfile.locations = list() passfile.locations.append( Location(35.611219, -97.467255, relevantText='Welcome to Garvey! Tap to scan your ID.', maxDistance=20)) passfile.locations.append( Location( 35.6115, -97.4695, relevantText='Welcome to the Branch! Tap to scan your ID.', maxDistance=20)) passfile.locations.append( Location(35.61201, -97.46850, relevantText='Welcome to the Brew! Tap to scan your ID.', maxDistance=20)) # passfile.locations.append(Location(35.613257, -97.467833, relevantText='Welcome to the PEC! Tap to scan your ID.', maxDistance=20)) passfile.ibeacons = list() passfile.ibeacons.append( IBeacon('1F234454-CF6D-4A0F-ADF2-F4911BA9FFA9', 1, 1, 'Tap to scan your ID.')) # Including the icon and logo is necessary for the passbook to be valid. passfile.addFile('icon.png', open('base.pass/icon.png', 'rb')) passfile.addFile('*****@*****.**', open('base.pass/[email protected]', 'rb')) passfile.addFile('*****@*****.**', open('base.pass/[email protected]', 'rb')) passfile.addFile('logo.png', open('base.pass/logo.png', 'rb')) passfile.addFile('*****@*****.**', open('base.pass/[email protected]', 'rb')) passfile.addFile('*****@*****.**', open('base.pass/[email protected]', 'rb')) try: # Add user photo with different device resolution support response = requests.get(user_pass.photo_URL) img = Image.open(BytesIO(response.content)) # Add 3x resolution thumbnail img = img.resize((204, 270)) img_byte = BytesIO() img.save(img_byte, format='PNG') passfile.addFile('*****@*****.**', img_bytes=img_byte.getvalue()) # Add 2x resolution thumbnail img = img.resize((136, 180)) img_byte = BytesIO() img.save(img_byte, format='PNG') passfile.addFile('*****@*****.**', img_bytes=img_byte.getvalue()) # Add 1x resolution thumbnail img = img.resize((68, 90)) img_byte = BytesIO() img.save(img_byte, format='PNG') passfile.addFile('thumbnail.png', img_bytes=img_byte.getvalue()) except: # Include default identification photo passfile.addFile('thumbnail.png', open('base.pass/thumbnail.png', 'rb')) passfile.addFile('*****@*****.**', open('base.pass/[email protected]', 'rb')) passfile.addFile('*****@*****.**', open('base.pass/[email protected]', 'rb')) # Create and output the Passbook file (.pkpass) passfile.create(config.PASS_TYPE_CERTIFICATE_PATH, config.PASS_TYPE_CERTIFICATE_PATH, config.WWDR_CERTIFICATE_PATH, config.PEM_PASSWORD, 'passes/' + user_pass.serial_number + '.pkpass')
def submit(request: Request, idNum: str = Form(...), idPin: str = Form(...), db: Session = Depends(get_db)): ''' Login Sumbitted, Validates User & Creates Pass ''' # get user data from form entered_id_num = idNum entered_id_pin = idPin logger.debug('Registration submitted for ID (' + entered_id_num + ') with Pin (' + entered_id_pin + ')') if entered_id_num in config.WHITELIST or not config.WHITELIST: if utils.input_validate(entered_id_num, entered_id_pin): # if user form data passes server-side validation, # check for existing pass db_pass = crud.get_pass(db, entered_id_num) if not db_pass: # if pass for user does not exist, # check for vaild User though OC user = schemas.User(entered_id_num, entered_id_pin) if user.is_valid(): # add user_pass to database db_pass = crud.add_pass(db, user) # create pass for given user schemas.Pkpass(db, db_pass.serial_number) google = schemas.JWT(db, db_pass.serial_number) # respond with success page with Add to Apple Wallet button response = templates.TemplateResponse('success.html', \ {'request': request, 'pass_hash': db_pass.pass_hash, 'jwt': google.get_link()}) logger.info('Pass created for ID (' + entered_id_num + ')') else: # user not valid through OC, # return login page with feedback del user response = templates.TemplateResponse('index.html', \ {'request': request, 'feedback': 'The ID Number and ID Card Pin Number entered do not match. Please try again.', 'entered_id': entered_id_num}) logger.debug('Registration unsuccessful for ID (' + entered_id_num + ') with Pin (' + entered_id_pin + ')') elif db_pass.id_pin == entered_id_pin: google = schemas.JWT(db, db_pass.serial_number) # pass for user already exists and login is correct, # respond with success page with Add to Apple Wallet button response = templates.TemplateResponse('success.html', \ {'request': request, 'pass_hash': db_pass.pass_hash, 'jwt': google.get_link()}) logger.info('Existing user successful request for ID (' + entered_id_num + ')') else: # pass for user already exists but login is incorrect, # user not valid through server quick validation response = templates.TemplateResponse('index.html', \ {'request': request, 'feedback': 'The ID Number and ID Card Pin Number entered do not match. Please try again.', 'entered_id': entered_id_num}) logger.debug('Registration unsuccessful for ID (' + entered_id_num + ') with Pin (' + entered_id_pin + ')') else: # user not valid through server quick validation response = templates.TemplateResponse('index.html', \ {'request': request, 'feedback': 'The ID Number and ID Card Pin Number entered do not match. Please try again.', 'entered_id': entered_id_num}) logger.debug('Registration unsuccessful for ID (' + entered_id_num + ') with Pin (' + entered_id_pin + ')') else: # entered ID num is not on whitelist response = templates.TemplateResponse('index.html', \ {'request': request, 'beta': 'The beta-testing program is currently invite-only.'}) logger.debug('Registration unsuccessful for ID (' + entered_id_num + ') with Pin (' + entered_id_pin + ')') return response