Skip to content

Commit

Permalink
Database Uplink + Password Hashing
Browse files Browse the repository at this point in the history
- Added MongoDB database and linked both users and animal data to the database for better security
- Converted the user password to hash
- Looking into possibly converting the animal data into hash for better protection
- Fully implemented audit logs
- Added way for user to input their own URI link to their own database and made trigger for if to show the prompt
- Minor fixes like removing the microseconds in the audit for better readability, added exit commands for certain actions and added max attempts for the login and sudo login functions.
- Added credential and level validation so now if a low level user attempts to use higher command, admin is notified (via audit trail for now)
- Finally added a way to edit animal entries
  • Loading branch information
tylerlight071 committed Jan 30, 2024
1 parent 268f765 commit 32f1705
Show file tree
Hide file tree
Showing 12 changed files with 563 additions and 221 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,5 +160,5 @@ cython_debug/
#.idea/

# User Files
*.json
*.txt
audit_log.txt
config.py
99 changes: 74 additions & 25 deletions FurEver_Friends.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,79 @@
import os
import json
import time
from colorama import Fore, Style
from view_animals import view_animals
from add_animal import add_animal
from change_adopted_status import change_adopted_status
from common_functions import clear_screen, log_action
from common_functions import clear_screen, log_action, generate_salt, hash_password, load_animal_data
from login import login
from edit_animal_entries import modify_animal
from pymongo import MongoClient
from config import mongodb_uri

# File paths for user and animal data
USER_DATA_FILE = "users.json"
ANIMAL_DATA_FILE = "animals.json"

# Default user data if files do not exist

# Check if config.py exists, if not, prompt the user to enter MongoDB URI and create it
if os.path.isfile('config.py'):
# Read the contents of the config file:
with open('config.py', 'r') as f:
config_content = f.read()
# Check f the URI has been inputted in the file
if 'URI Inputted' in config_content:
uri_inputted = True
else:
uri_inputted = False
else:
uri_inputted = False

if not uri_inputted:
mongodb_uri = input("Please enter your MongoDB connection URI: ")
# Update config file to indicate that URI has been inputted
with open('config.py', 'w') as f:
f.write(f"mongodb_uri = '{mongodb_uri}'\n")
f.write("# URI Inputted\n")

uri = mongodb_uri
client = MongoClient(uri)
db = client['animal_rescue']
users_collection = db['users']

# Check if client is connected
if client is not None:
print("Connected to MongoDB successfully.")
time.sleep(2)
else:
print("Failed to connect to MongoDB.")
time.sleep(2)
else:
pass

# Connect to MongoDB
uri = mongodb_uri
client = MongoClient(uri)
db = client['animal_rescue']
users_collection = db['users']

# Default password
default_password = "ADMIN"

# Generate salt and hash password
salt = generate_salt()
hashed_password = hash_password(default_password, salt)

salt_hex = salt.hex()

# Default user data if collection do not exist
DEFAULT_USER_DATA = {
"ADMIN": {
"password": "ADMIN",
"level": 3
}
"username": "ADMIN",
"hashed_password": hashed_password,
"salt": salt_hex,
"level": 3
}

DEFAULT_ANIMAL_DATA = {}

def main():
clear_screen()

# Check if user.json exists, if not, create it with default data
if not os.path.exists(USER_DATA_FILE):
with open(USER_DATA_FILE, 'w') as user_file:
json.dump(DEFAULT_USER_DATA, user_file, indent=4)

# Check if animals.json exists, if not, create it with default data
if not os.path.exists(ANIMAL_DATA_FILE):
with open(ANIMAL_DATA_FILE, 'w') as animal_file:
json.dump(DEFAULT_ANIMAL_DATA, animal_file, indent=4)

try:
while True:
# Display main menu options
Expand All @@ -46,8 +85,8 @@ def main():
if choice == '1':
clear_screen()
# Pull the username and user level from the login function
username, user_level = login()
if username is not None:
current_user, user_level = login()
if current_user is not None:
while True:
clear_screen()
# Display main menu after successful login
Expand All @@ -73,17 +112,23 @@ def main():
option = input("\nPlease select an option: ")

if option == '1':
time.sleep(1)
log_action(current_user, "Entered, 'Animal Database" )
view_animals()
elif option == '2' and user_level >= 2:
time.sleep(1)
log_action(current_user, "Entered 'Add an animal'")
add_animal()
elif option == '3' and user_level >= 3:
time.sleep(1)
change_adopted_status()
elif option == '4' and user_level >= 3:
print("\nFeature coming soon")
time.sleep(2)
time.sleep(1)
modify_animal()
elif option == str(option_counter) and user_level >= 1:
print("\nLogging out...")
time.sleep(2)
log_action(current_user, f"Logged Out")
clear_screen()
break
else:
Expand All @@ -104,4 +149,8 @@ def main():
time.sleep(2)

if __name__ == "__main__":
# Check if the users collection exists, if not, create it with default values
collection_names = db.list_collection_names()
if 'users' not in collection_names:
users_collection.insert_one(DEFAULT_USER_DATA)
main()
51 changes: 36 additions & 15 deletions add_animal.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import time
from colorama import Fore, Style
from common_functions import clear_screen, load_data, save_data, log_action
from common_functions import clear_screen, log_action, hash_animal_data, generate_salt
from sudo_user import sudo_user
from pymongo import MongoClient
from config import mongodb_uri

ANIMAL_DATA_FILE = "animals.json"
# Connect to MongoDB
uri = mongodb_uri
client = MongoClient(uri)

def add_animal():
# Load animal data from file
animals = load_data(ANIMAL_DATA_FILE)
db = client['animal_rescue']
animals_collection = db['animals']

def add_animal():
# Continuous loop for adding animals
while True:
clear_screen()
Expand All @@ -18,7 +22,7 @@ def add_animal():

print("Enter animal details or type 'exit' to cancel:")

#Input fields for animal data
# Input fields for animal data
name = input(Fore.CYAN + "Name: " + Style.RESET_ALL).strip()

# Check if user wants to exit
Expand Down Expand Up @@ -46,27 +50,44 @@ def add_animal():

age = int(age)

# Add animals to the data dictionary
animals[name] = {
# Make the user verify their identity
current_user = sudo_user()

# Generate salt
salt = generate_salt()

# Hash the new animal data with the salt
hashed_animal_data = hash_animal_data({
'name': name,
'species': species,
'breed': breed,
'gender': gender,
'age': age,
'adopted': False
}
}, salt)

# Make the user verify their identity
current_user = sudo_user()
# Store the salt in hexadecimal format
salt_hex = salt.hex()

save_data(animals, ANIMAL_DATA_FILE)
# Add hashed animal data to the animals dictionary
animals_collection.insert_one ({
'name': name,
'species': species,
'breed': breed,
'gender': gender,
'age': age,
'adopted': False,
'salt': salt_hex,
'hashed_animal_data': hashed_animal_data,
})

# Log the action of adding animal into the audit file
log_action(current_user, f"Added animal: {name}")
# Log the action of adding the animal into the audit file
log_action(current_user, f"Added animal: {name}, {species}, {breed}")

# Confirm successful addition of the animal
print(Fore.GREEN + "\n✨ Animal added successfully! ✨" + Style.RESET_ALL)
log_action(current_user, f"Exited 'Add an animal'")
time.sleep(2)

# Exit the loop after successful addition
break
break
5 changes: 4 additions & 1 deletion admin_dashboard.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import time
from colorama import Fore, Style
from common_functions import clear_screen, load_data, save_data
from common_functions import clear_screen, log_action
from register import register
from user_management import user_management


USER_DATA_FILE = "users.json"

def admin_dashboard():
Expand All @@ -28,6 +29,8 @@ def admin_dashboard():
time.sleep(2)
elif option == '4':
print("\nLogging out...")
log_action("ADMIN", "Logged Out")
time.sleep(2)
exit()
else:
print(Fore.RED + "\nInvalid option. Please try again." + Style.RESET_ALL)
Expand Down
57 changes: 36 additions & 21 deletions change_adopted_status.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,49 @@
import json
import os
import time
from colorama import Fore, Style
from common_functions import clear_screen, load_data, save_data
from sudo_user import sudo_user
from common_functions import clear_screen, load_animal_data, save_data, log_action
from pymongo import MongoClient
from config import mongodb_uri

ANIMAL_DATA_FILE = "animals.json"
# Connect to MongoDB
uri = mongodb_uri
client = MongoClient(uri)

db = client['animal_rescue']
animals_collection = db['animals']

def change_adopted_status():
clear_screen()

# Load animal data from file
animals = load_data(ANIMAL_DATA_FILE)
animals = load_animal_data(animals_collection)
current_user = sudo_user()

name = input("\nEnter the name of the animal to toggle adoption status: ")
name = input("\nEnter the name of the animal to toggle adoption status (type 'exit' to leave): ")

# Check if animal exists in the data
if name in animals:

# Toggle the adopted status
animals[name]['adopted'] = not animals[name].get('adopted', False)
save_data(animals, ANIMAL_DATA_FILE)

# Notify the user about the updated adoptino status
if animals[name]['adopted']:
print(Fore.GREEN + f"\n{name} has been marked as " + Fore.CYAN + "adopted!" + Style.RESET_ALL)
else:
print(Fore.GREEN + f"\n{name} has been marked as " + Fore.RED + "not adopted!" + Style.RESET_ALL)
if name == "exit":
log_action(current_user, "Exited 'Change Adoption Status'")
print("\nExiting...")
time.sleep(2)
return

# Notify the user if the animal is not found
else:
print(Fore.RED + f"\nNo animal found with the name {name}" + Style.RESET_ALL)
# Check if animal exists in the data
if name in animals:

# Toggle the adopted status
animals[name]['adopted'] = not animals[name].get('adopted', False)
save_data(animals)

# Notify the user about the updated adoptino status
if animals[name]['adopted']:
print(Fore.GREEN + f"\n{name} has been marked as " + Fore.CYAN + "adopted!" + Style.RESET_ALL)
log_action(current_user, f"{name} marked as adopted")
else:
print(Fore.GREEN + f"\n{name} has been marked as " + Fore.RED + "not adopted!" + Style.RESET_ALL)
log_action(current_user, f"{name} marked as not adopted")
# Notify the user if the animal is not found
else:
print(Fore.RED + f"\nNo animal found with the name {name}" + Style.RESET_ALL)

time.sleep(2)
time.sleep(2)
Loading

0 comments on commit 32f1705

Please sign in to comment.