def post(self, email: str): # Check if this user exists db = Mysql() user = db.execute_select( "SELECT u.`id`, up.`first_name` FROM `user` AS u LEFT JOIN `user_profile` AS up ON up.`user_id` = u.`id` WHERE u.`email` = %s", (email, )) if len(user) == 0: return { 'message': 'A user with that email does not exist', 'error_code': 'user_does_not_exist' }, 400 # Set forgotten password key and return it forgotten_password_key = generate_activation_key() forgotten_password_key_expires_on = ( datetime.datetime.now() + relativedelta(weeks=self.forgotten_password_key_expiration_period) ).strftime('%Y-%m-%d 00:00:00') db.execute( "UPDATE `user` SET `forgotten_password_key` = %s, `forgotten_password_key_expires_on` = %s WHERE `email` = %s", (forgotten_password_key, forgotten_password_key_expires_on, email)) # Send activation request mail mailer = Mailer() mailer.send( 'user_password_reset', email, USERS_FIRST_NAME=user[0]['first_name'], USER_FORGOTTEN_PASSWORD_URL=config['general']['public_domain'] + '/u/forgotten/password/key' + forgotten_password_key) return {'password_reset_key': forgotten_password_key}, 200
def is_token_revoked(decoded_token): """ Checks if the given token is revoked or not. Because we are adding all the tokens that we create into this database, if the token is not present in the database we are going to consider it revoked, as we don't know where it was created. :param decoded_token: """ jti = decoded_token['jti'] cache = Cache() if cache.get('token_' + jti) is None: return True token = eval(cache.get('token_' + jti).decode("utf-8")) if token is not None: return token['revoked'] else: db = Mysql() token = db.execute_select( "SELECT * FROM `user_session` WHERE `jti` = %s", (jti, )) if len(token) > 0: return token[0]['revoked'] else: return True
def post(self): # Get and validate input data _user_parser = reqparse.RequestParser() _user_parser.add_argument('email', type=str, required=True, location="json") _user_parser.add_argument('password', type=str, required=False, location="json") _user_parser.add_argument('facebook_id', type=str, required=False, location="json") data = _user_parser.parse_args() if data['facebook_id'] is None and data['password'] is None: return { 'message': 'Invalid input data.', 'error_code': 'invalid_request' }, 400 # Check if this user already exists db = Mysql() user = db.execute_select("SELECT * FROM `user` WHERE `email` = %s", (data['email'], )) if len(user) > 0: if user[0]['facebook_id'] == "" and data['facebook_id'] is not None: db.execute( "UPDATE `user` SET `facebook_id` = %s WHERE `email` = %s", (data['facebook_id'], data['email'])) return { 'message': 'A user with that email already exists', 'error_code': 'user_exists' }, 400 # Create user if data['facebook_id'] is not None: # facebook_id db.execute( "INSERT INTO `user` (`email`, `facebook_id`, `is_active`) VALUES(%s, %s, %s)", (data['email'], data['facebook_id'], 1)) else: activation_key = generate_activation_key() db.execute( "INSERT INTO `user` (`email`, `password`, `activation_key`) VALUES(%s, %s, %s)", (data['email'], generate_password_hash( data['password']), activation_key)) # Send activation mail mailer = Mailer() mailer.send('user_register_and_activation', data['email'], USER_ACTIVATION_URL=config['general']['public_domain'] + '/u/activation/key' + activation_key, USER_EMAIL=data['email']) return [], 201
def add_token_to_database(encoded_token, identity_claim): """ Adds a new token to the cache and database. It is not revoked when it is added. :param encoded_token: :param identity_claim: """ # Prepare decoded_token = decode_token(encoded_token) jti = decoded_token['jti'] token_type = decoded_token['type'] user_identity = decoded_token[identity_claim] revoked = False expires = _epoch_utc_to_datetime(decoded_token['exp']) # Save cache = Cache() cache.set( 'token_' + jti, str({ 'jti': jti, 'token_type': token_type, 'user_identity': user_identity, 'revoked': revoked, 'expires': expires })) db = Mysql() db.execute( "INSERT INTO `user_session` (`user_id`,`jti`,`token_type`,`user_identity`,`revoked`,`expires`) VALUES (%s,%s,%s,%s,%s,%s)", (user_identity, jti, token_type, user_identity, revoked, expires))
def post(self): # Validate and get input vars _user_parser = reqparse.RequestParser() _user_parser.add_argument('email', type=str, required=True) _user_parser.add_argument('password', type=str, required=False) _user_parser.add_argument('facebook_id', type=str, required=False) data = _user_parser.parse_args() if data['facebook_id'] is None and data['password'] is None: return { 'message': 'Invalid input data.', 'error_code': 'invalid_request' }, 400 # Check user db = Mysql() user = db.execute_select( "SELECT `id`, `facebook_id`, `password` FROM `user` WHERE `email` = %s LIMIT 1", (data['email'], )) if data['facebook_id'] is not None: if len(user ) == 1 and user[0]['facebook_id'] == data['facebook_id']: pass else: return { 'message': 'Invalid email or password.', 'error_code': 'invalid_credentials' }, 401 else: if len(user) == 1 and check_password_hash(user[0]['password'], data['password']): pass else: return { 'message': 'Invalid email or password.', 'error_code': 'invalid_credentials' }, 401 # Create the JWT tokens expires_delta = datetime.timedelta( days=config['auth']['access_token']['expires_delta']) access_token = create_access_token(identity=user[0]['id'], expires_delta=expires_delta) refresh_token = create_refresh_token(identity=user[0]['id'], expires_delta=expires_delta) # Store the tokens in our store with a status of not currently revoked. add_token_to_database(access_token, 'identity') # app.config['JWT_IDENTITY_CLAIM'] add_token_to_database(refresh_token, 'identity') # app.config['JWT_IDENTITY_CLAIM'] return { 'access_token': access_token, 'refresh_token': refresh_token }, 201
def generate_forgotten_password_key(): ''' Generates an unique forgotten password key. ''' forgotten_password_key = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(20)) db = Mysql() user = db.execute_select("SELECT u.`id` FROM `user` AS u WHERE u.`forgotten_password_key` = %s", (forgotten_password_key,)) if len(user) > 0: forgotten_password_key = generate_forgotten_password_key() return forgotten_password_key
def generate_activation_key(): ''' Generates an unique activation key. ''' activation_key = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(20)) db = Mysql() user = db.execute_select("SELECT u.`id` FROM `user` AS u WHERE u.`activation_key` = %s", (activation_key,)) if len(user) > 0: activation_key = generate_activation_key() return activation_key
def get_user_tokens(user_identity): """ Returns all of the tokens, revoked and unrevoked, that are stored for the given user :param user_identity: """ db = Mysql() return db.execute_select( "SELECT * FROM `user_session` WHERE `user_identity` = %s", (user_identity, ))
def get(self): user_identity = get_jwt_identity() db = Mysql() user = db.execute_select( "SELECT up.*, u.`email` FROM `user_profile` AS up LEFT JOIN `user` AS u ON u.`id` = up.`user_id` WHERE up.`user_id` = %s", (user_identity, )) if len(user) == 0: return { 'message': 'User not found.', 'error_code': 'user_not_found' }, 400 return user[0]
def get(self): user_identity = get_jwt_identity() db = Mysql() user = db.execute_select( "SELECT `profile_image_url` FROM `user_profile` WHERE `user_id` = %s", (user_identity, )) if user[0]['profile_image_url'] != '': return {'profile_image_url': user[0]['profile_image_url']} return { 'profile_image_url': config['general']['file_storage'] + 'default.jpg' }
def put(self): # Validate the input data _user_parser = reqparse.RequestParser() _user_parser.add_argument('first_name', type=str, required=False, location="json") _user_parser.add_argument('last_name', type=str, required=False, location="json") _user_parser.add_argument('headline', type=str, required=False, location="json") _user_parser.add_argument('country_id', type=str, required=False, location="json") _user_parser.add_argument('city_id', type=str, required=False, location="json") data = _user_parser.parse_args() # Prepare the data fields = [] values = () vars = ['first_name', 'last_name', 'headline', 'country_id', 'city_id'] for var in vars: if data[var] is not None: fields.append('`' + var + '` = %s') values = (*values, data[var]) if len(fields) == 0: return { 'message': 'Invalid input data.', 'error_code': 'invalid_request' }, 400 # Update user user_identity = get_jwt_identity() values = (*values, user_identity) db = Mysql() db.execute( "UPDATE `user_profile` SET " + ', '.join(fields) + " WHERE `user_id` = %s", values) return [], 200
def post(self): # Get input data _user_parser = reqparse.RequestParser() _user_parser.add_argument('file', type=FileStorage, required=True, location='files') data = _user_parser.parse_args() # Get file file = data['file'] name, ext = os.path.splitext(file.filename) # Check if file covers the requirements if ext not in self.file_extensions: return { 'message': 'The file extension is not allowed.', 'error_code': 'file_extension_not_allowed' }, 400 # Create path where we will save the file directory = config['general']['file_storage'] + datetime.datetime.now( ).strftime(("%Y/%m")) basedir = os.path.abspath(os.path.dirname(__file__)) save_path = basedir + directory if not os.path.exists(save_path): os.makedirs(save_path) # Save file new_file_name = datetime.datetime.now().strftime(("%Y%m%d")) + ''.join( random.choice(string.ascii_uppercase + string.digits) for _ in range(20)) + ext file_path = "{path}/{file}".format(path=save_path, file=new_file_name) file.save(file_path) # Update profile image of the user in the database file_url = "{directory}/{file}".format(directory=directory, file=new_file_name) user_identity = get_jwt_identity() db = Mysql() db.execute( "UPDATE `user_profile` SET `profile_image_url` = %s WHERE `user_id` = %s", ( file_url, user_identity, )) return [], 200
def prune_database(): """ Delete tokens that have expired from the database. How (and if) you call this is entirely up you. You could expose it to an endpoint that only administrators could call, you could run it as a cron, set it up with flask cli, etc. """ now = datetime.now() db = Mysql() expired_tokens = db.execute_select( "SELECT * FROM `user_session` WHERE `expires` < NOW()", (now, )) for expired_token in expired_tokens: cache = Cache() cache.delete('token_' + expired_token['jti']) db.execute("DELETE FROM `user_session` WHERE `expires` < %s", (now, ))
def put(self, activation_key: str): # Check if this user already exists db = Mysql() user = db.execute_select( "SELECT * FROM `user` WHERE `activation_key` = %s", (activation_key, )) if len(user) == 0: return { 'message': 'The activation key is invalid.', 'error_code': 'invalid_activation_key' }, 400 db.execute( "UPDATE `user` SET `activation_key` = %s, `is_active` = %s WHERE `activation_key` = %s", ('', 1, activation_key)) return [], 200
def delete(self): user_identity = get_jwt_identity() db = Mysql() user = db.execute_select( "SELECT `profile_image_url` FROM `user_profile` WHERE `user_id` = %s", (user_identity, )) if user[0]['profile_image_url'] != '': basedir = os.path.abspath(os.path.dirname(__file__)) delete_path = basedir + user[0]['profile_image_url'] os.remove(delete_path) db.execute( "UPDATE `user_profile` SET `profile_image_url` = %s WHERE `user_id` = %s", ( '', user_identity, )) return [], 200
def delete_tokens(token_ids_list, user_identity): """ Delete tokens. :param tokens: List of tokens :type tokens: list """ # Delete from cache db = Mysql() tokens = db.execute_select( "SELECT `jti` FROM `user_session` WHERE `user_id` = %s AND `id` IN (%s)", (user_identity, token_ids_list)) if len(tokens) > 0: for token in tokens: cache = Cache() cache.delete('token_' + token['jti']) # Delete from DB db.execute( "DELETE FROM `user_session` WHERE `user_id` = %s AND `id` IN (%s)", (user_identity, token_ids_list))
def put(self, password_reset_key: str): # Check if this user already exists db = Mysql() user = db.execute_select( "SELECT * FROM `user` WHERE `forgotten_password_key` = %s AND `forgotten_password_key_expires_on` > NOW()", (password_reset_key, )) if len(user) == 0: return { 'message': 'The password reset key is invalid or expired.', 'error_code': 'invalid_password_reset_key' }, 400 # Get password and password confirm and check their validity _user_parser = reqparse.RequestParser() _user_parser.add_argument("password", type=str, required=True, location="json") _user_parser.add_argument("password_confirm", type=str, required=True, location="json") data = _user_parser.parse_args() if data['password'] != data['password_confirm'] or len( data['password']) < 8 or len(data['password']) > 255: return { 'message': 'Invalid input data.', 'error_code': 'invalid_request' }, 400 # Update users password db.execute( "UPDATE `user` SET `password` = %s, `forgotten_password_key` = %s, `forgotten_password_key_expires_on` = %s WHERE `forgotten_password_key` = %s", (generate_password_hash( data['password']), '', '', password_reset_key)) return [], 200
def revoke_token(token_id, user_identity): """ Revokes the given token. Raises a TokenNotFound error if the token does not exist in the database. :param token_id: Id of the token :param user_identity: :type token_id: int """ db = Mysql() token = db.execute_select( "SELECT * FROM `user_session` WHERE `user_identity` = %s AND `id` = %s", (user_identity, token_id)) if len(token) > 0: token = token[0] token['revoked'] = 1 cache = Cache() cache.set('token_' + token['jti'], str(token)) db.execute( "UPDATE `user_session` SET `revoked` = %s WHERE `user_identity` = %s AND `id` = %s", (1, user_identity, token_id)) else: return False
def get(self, email: str): # Check if this user already exists db = Mysql() user = db.execute_select( "SELECT u.`activation_key`, up.`first_name` FROM `user` AS u LEFT JOIN `user_profile` AS up ON up.`user_id` = u.`id` WHERE u.`email` = %s", (email, )) if len(user) == 0: return { 'message': 'A user with that email does not exist', 'error_code': 'user_does_not_exist' }, 400 # Send activation request mail mailer = Mailer() mailer.send('user_request_activation', email, USERS_FIRST_NAME=user[0]['first_name'], USER_EMAIL=email, USER_ACTIVATION_URL=config['general']['public_domain'] + '/u/activation/key' + user[0]['activation_key']) return {'activation_key': user[0]['activation_key']}, 200