コード例 #1
0
 def post(cls):
     public_key = crypto_handler.pub_key()
     parms = request.get_json()
     print(f"Received register request: {parms}")
     if 'hwid' in parms and 'pkey' in parms:
         # Check if we have a safe with this id already
         this_safe = SafeModel.find_by_id(parms['hwid'])
         if this_safe:
             # We already know about this safe
             logging.info(
                 f"SAFE: Rejected repeated safe registration hw_id: {parms['hwid']}"
             )
             return {"error": msgs.SAFE_REGISTRATION_ERROR}, 400
         else:
             now = datetime.utcnow()
             this_safe = SafeModel(hardware_id=parms['hwid'],
                                   public_key=parms['pkey'],
                                   last_update=now,
                                   unlock_time=now)
             this_safe.save_to_db()
             logging.info(
                 f"SAFE: Registered new safe with hw_id: {parms['hwid']}")
             return {"key": public_key.decode("utf-8")}, 200
     else:
         return {"error": msgs.SAFE_REGISTRATION_ERROR}, 400
コード例 #2
0
 def get(cls):
     """
     :parameter
     """
     this_user_id = get_jwt_identity()
     this_user = UserModel.find_by_id(this_user_id)
     relationship_list = RelationshipModel.find_any(this_user_id)
     safe_list = SafeModel.find_by_safeholder_id(this_user_id)
     summary_list = []
     safe_ids = []
     # First scan the safes in relationships
     for relationship in relationship_list:
         safe_ids.append(relationship.safe.hardware_id)
         summary_item = {
             'hardware_id':
             relationship.safe.hardware_id,
             'locked':
             all([
                 relationship.safe.hinge_closed,
                 relationship.safe.lid_closed,
                 relationship.safe.bolt_engaged
             ]),
             'safeholder_displayname':
             relationship.safeholder.displayname,
             'keyholder_displayname':
             relationship.keyholder.displayname
         }
         summary_list.append(summary_item)
     # then scan the safes not not in relationships
     for safe in safe_list:
         if safe.hardware_id not in safe_ids:
             summary_item = {
                 'hardware_id':
                 safe.hardware_id,
                 'locked':
                 all([
                     safe.hinge_closed, safe.lid_closed, safe.bolt_engaged
                 ]),
                 'safeholder_displayname':
                 this_user.displayname,
                 'keyholder_displayname':
                 None
             }
             summary_list.append(summary_item)
     return {
         "safe_list":
         [safe_summary_schema.dump(item) for item in summary_list]
     }, 200
コード例 #3
0
    def delete(cls):
        """
        Delete a relationship that already exists
        :return:
        """
        parms = kh_claim_sh_schema.load(request.get_json())
        safeholder = UserModel.find_by_displayname(parms['displayname'])
        keyholder = UserModel.find_by_id(get_jwt_identity())
        now = datetime.now(timezone.utc)
        # First check if the safeholder exists
        if not safeholder:
            return {
                "msg": msgs.USER_NONEXISTANT.format(parms['displayname'])
            }, 400
        # Now check if the safeholder has a safe - and its digital key matches
        safeholder_safes = SafeModel.find_by_safeholder_id(safeholder.id)
        if not safeholder_safes:  # Safeholder has no safes
            return {"msg": msgs.INCORRECT_KEY}, 401
        for safe in safeholder_safes:
            if safe.digital_key == parms['digital_key']:
                # We have a hit - now check if the KH is the KH for that relationship
                relationship_list = RelationshipModel.find_by_safe_id(
                    safe.hardware_id)
                for relationship in relationship_list:
                    if (relationship.keyholder_id == keyholder.id) and (
                            relationship.end_date is None):
                        # OK, we have an active relationship between this KH and the SH
                        # Set a new digital_key for the safe - and unlock it (for safety)
                        digital_key = str(uuid4())
                        safe.digital_key = digital_key
                        safe.auth_to_unlock = expression.true()
                        safe.unlock_time = now
                        safe.last_update = now
                        safe.save_to_db()
                        # Then terminate the relationship - and send a mail to the safeholder
                        relationship.end_date = date.today()
                        relationship.send_relationship_email(status='end')
                        relationship.save_to_db()
                        # Log the end of the relationship
                        logging.info(
                            f"RELATIONSHIP: END {relationship.keyholder.username} - "
                            f"{relationship.safeholder.username}")

                        return {"msg": msgs.RELATIONSHIP_TERMINATED}, 200
                return {"msg": msgs.INCORRECT_KH}, 401
        return {"msg": msgs.INCORRECT_KEY}, 401
コード例 #4
0
 def post(cls):
     """
     For a KH to claim a SH they must pass the SH displayname AND a correct digital key for that user's safe
     :param
     """
     parms = kh_claim_sh_schema.load(request.get_json())
     safeholder = UserModel.find_by_displayname(parms['displayname'])
     potential_kh = UserModel.find_by_id(get_jwt_identity())
     # First check if the safeholder exists
     if not safeholder:
         return {
             "msg": msgs.USER_NONEXISTANT.format(parms['displayname'])
         }, 400
     # Then check if potential KH is trying to claim themself as a SH
     if safeholder.id == potential_kh.id:
         return {"msg": msgs.KH_EQ_SH}, 401
     # Now check if the safeholder has a safe - and its digital key matches
     safeholder_safes = SafeModel.find_by_safeholder_id(safeholder.id)
     if not safeholder_safes:  # Safeholder has no safes
         return {"msg": msgs.INCORRECT_KEY}, 401
     for safe in safeholder_safes:
         if safe.digital_key == parms['digital_key']:
             # We have a hit - now check if the SH is not already in a relationship for that safe
             relationship_list = RelationshipModel.find_by_safe_id(
                 safe.hardware_id)
             for relationship in relationship_list:
                 if (relationship.safeholder_id == safeholder.id) and (
                         relationship.end_date is None):
                     return {"msg": msgs.SH_IN_RELATIONSHIP}, 401
             # OK now we can set up the relationship
             relationship = RelationshipModel(keyholder_id=potential_kh.id,
                                              safeholder_id=safeholder.id,
                                              safe_id=safe.hardware_id,
                                              start_date=date.today())
             relationship.save_to_db()
             # Send email to Safeholder to confirm start of relationship
             relationship.send_relationship_email(status='start')
             # Log the start of the relationship
             logging.info(
                 f"RELATIONSHIP: START {relationship.keyholder.username} - "
                 f"{relationship.safeholder.username}")
             return {"msg": msgs.RELATIONSHIP_ESTABLISHED}, 200
     # If we get here the safeholder had no safes that the digital key fitted
     return {"msg": msgs.INCORRECT_KEY}, 401
コード例 #5
0
 def post(cls):
     """
     Assign current logged on user as safeholder for the specified safe - assuming it is available
     Generate, save and return a digital key for the Safeholder to pass to their chosen Keyholder
     """
     parms = safe_claim_schema.load(request.get_json())
     this_user = UserModel.find_by_id(get_jwt_identity())
     # Check if we have this safe
     requested_safe = SafeModel.find_by_id(parms['hardware_id'])
     if not requested_safe:
         return {"error": msgs.CLAIM_NO_SAFE}, 400
     # Then check if the safe is already claimed
     if requested_safe.safeholder:
         return {"error": msgs.CLAIM_NOT_AVAILABLE}, 400
     # Allocate this user as safeholder - generate the digital key first
     requested_safe.safeholder = this_user
     digital_key = str(uuid4())
     requested_safe.digital_key = digital_key
     requested_safe.save_to_db()
     return {"digital_key": digital_key}, 200
コード例 #6
0
 def delete(cls):
     """
     Remove the current logged-on user as the holder of this safe - make it available for re-claiming
     """
     parms = safe_claim_schema.load(request.get_json())
     this_user = UserModel.find_by_id(get_jwt_identity())
     # Check if we have this safe
     requested_safe = SafeModel.find_by_id(parms['hardware_id'])
     if not requested_safe:
         return {"error": msgs.CLAIM_NO_SAFE}, 400
     # Next check if the safe is owned by this_user
     if requested_safe.safeholder is None:
         # The safe does not have an owner - cannot delete it
         return {"error": msgs.RELEASE_NOT_OWNED}, 400
     if requested_safe.safeholder.id != this_user.id:
         # The safe does not belong to you
         return {"error": msgs.RELEASE_NOT_OWNED}, 400
     # TODO - Need to also check if the safeholder is in a relationship - should not permit release if so
     # OK, we can remove the safeholder
     requested_safe.safeholder = None
     requested_safe.save_to_db()
     return {"msg": "ok"}, 200
コード例 #7
0
 def post(cls):
     valid_request = False
     now = datetime.utcnow()
     parms = request.get_json()
     if all(map(lambda x: x in parms, ['hwid', 'sig', 'msg'])):
         # Check if we have a safe with this ID
         this_safe = SafeModel.find_by_id(parms['hwid'])
         if this_safe:
             # Check message is valid
             msg_valid, message = crypto_handler.decrypt(
                 msg=parms['msg'],
                 sig=parms['sig'],
                 pkey=this_safe.public_key)
             if msg_valid:
                 # Interpret content and update database
                 # Remember, message may be multiple lines
                 print(f"Message received = {message}")
                 if '\n' in message:
                     message_lines = message.split('\n')
                 else:
                     message_lines = [
                         message,
                     ]
                 status_parts = message_lines[0].split(',')
                 if len(status_parts) == 6:
                     this_safe.last_update = convert_timestamp(
                         status_parts[2])
                     if status_parts[3] == 'True':
                         this_safe.hinge_closed = True
                     if status_parts[4] == 'True':
                         this_safe.lid_closed = True
                     if status_parts[5] == 'True':
                         this_safe.bolt_engaged = True
                     # Now check if there are any time-based updates to make
                     if this_safe.unlock_time < now:
                         this_safe.auth_to_unlock = True
                     this_safe.save_to_db()
                     if len(message_lines) > 1:
                         # Add entries to Safe events database
                         for i in range(len(message_lines) - 1):
                             if message_lines[i + 1].startswith('EVENT'):
                                 event_parts = message_lines[i +
                                                             1].split(',')
                                 if len(event_parts) == 3:
                                     event = SafeEventModel(
                                         hardware_id=this_safe.hardware_id,
                                         event_code=event_codes.get(
                                             event_parts[2], DEFAULT_EVENT),
                                         detail=event_parts[2],
                                         timestamp=convert_timestamp(
                                             event_parts[1]))
                                     event.save_to_db()
                     logging.info(
                         f"Safe parameters updated for {this_safe.hardware_id}"
                     )
                     # Now construct a response, encrypt, sign and return it
                     server_message_base = 'Auth_to_unlock:{}:{}\nUnlock_time:{}\nSettings:SCANFREQ={' \
                                           '}:REPORTFREQ={}:PROXIMITYUNIT={}:DISPLAYPROXIMITY={}'
                     if this_safe.auth_to_unlock:
                         auth_msg = 'TRUE'
                     else:
                         auth_msg = 'FALSE'
                     if this_safe.display_proximity:
                         disp_msg = 'TRUE'
                     else:
                         disp_msg = 'FALSE'
                     server_message = server_message_base.format(
                         auth_msg, now, this_safe.unlock_time,
                         this_safe.scan_freq, this_safe.report_freq,
                         this_safe.proximity_unit, disp_msg)
                     msg_enc_64, msg_sig_64 = crypto_handler.encrypt(
                         msg=server_message, safe_pkey=this_safe.public_key)
                     return {
                         "msg": msg_enc_64.decode('utf-8'),
                         "sig": msg_sig_64.decode('utf-8')
                     }, 200
             else:
                 logging.info(
                     f"Invalid checking message received from {parms['hwid']}"
                 )
                 return {"error": msgs.SAFE_CHECKIN_ERROR}, 400
         else:
             return {"error": msgs.SAFE_CHECKIN_ERROR}, 400
     logging.info(f"Improperly formed checkin request: {parms}")
     return {"error": msgs.SAFE_CHECKIN_ERROR}, 400
コード例 #8
0
 def get(clscls):
     return {
         "safes":
         [safe_schema.dump(safe) for safe in SafeModel.find_available()]
     }