Compare commits

...

20 Commits

Author SHA1 Message Date
Aiirondev_dev df5a3265a1 fix of the file upload in the edet_item funktions 2026-08-01 18:35:28 +02:00
Aiirondev_dev 2c6da44af8 fix of the file upload 2026-08-01 18:02:01 +02:00
Aiirondev_dev d6d6858002 new implementation of file uploading 2026-08-01 10:34:59 +02:00
Aiirondev_dev 0cf0bd8dec new implementation of file uploading 2026-08-01 00:00:18 +02:00
Aiirondev_dev af9826547c Incorperation of the right fallback path for the image folders 2026-07-31 22:43:48 +02:00
Aiirondev_dev 87027cf3c9 Changes to the damaged Items 2026-07-31 22:12:19 +02:00
Aiirondev_dev 46f79b002b Changes to the admin_damaged_items 2026-07-31 21:40:20 +02:00
Aiirondev_dev dcc8aae650 Implemenmtation of the new encryption into the my borrowed funktion 2026-07-31 21:28:34 +02:00
Aiirondev_dev c9fdf41e0b Implemenmtation of the new encryption into the my borrowed funktion 2026-07-31 21:21:43 +02:00
Aiirondev_dev 3e26c691b1 Addition of backwarts compatibility 2026-07-31 20:57:19 +02:00
Aiirondev_dev 6c3d62e84b Fix of a NoneType has no attribute 'get' Error 2026-07-31 20:50:58 +02:00
Aiirondev_dev 88f6c3c0f5 backwarts compaibility and integration of encryption in the manage-tenant.sh 2026-07-31 20:45:16 +02:00
Aiirondev_dev 08d8b78d91 changes to the encryption to the users and Items 2026-07-31 20:16:34 +02:00
Aiirondev_dev 258e287a39 changes to the encryption to the users and Items 2026-07-31 19:55:59 +02:00
Aiirondev_dev 9b12d9a3d0 changes to the encryption to the users and Items 2026-07-31 15:15:09 +02:00
Aiirondev_dev 237788af96 implementation of encryption for the username to avoid any recognission potetioal 2026-07-31 13:12:46 +02:00
Aiirondev_dev 7368d82fc4 changes to fully incorpereate the school info managment back into the working system 2026-07-30 23:55:46 +02:00
Aiirondev_dev 3e5e243ddf changes to the autorisation system for the page autorisation to have a continued line of page authentification and no discrepencies in the Software it self 2026-07-30 23:34:06 +02:00
Aiirondev_dev 8176cea8fe change of the HTML Signature 2026-07-30 16:52:34 +02:00
Aiirondev_dev b71f2e9089 changes 2026-07-30 01:09:58 +02:00
11 changed files with 1149 additions and 6699 deletions
+443 -873
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+205 -173
View File
@@ -19,11 +19,61 @@ Collection Structure:
- Status fields: Verfuegbar, User (if currently borrowed) - Status fields: Verfuegbar, User (if currently borrowed)
""" """
from bson.objectid import ObjectId from bson.objectid import ObjectId
from bson.errors import InvalidId
import datetime import datetime
import Web.modules.database.settings as cfg import Web.modules.database.settings as cfg
from Web.modules.database.settings import MongoClient from Web.modules.database.settings import MongoClient
import Web.modules.inventarsystem.data_protection as dp
def safe_decrypt_user(encrypted_user):
"""
Safely decrypt an encrypted username string.
Returns the original string if decryption fails or if input is empty/None.
"""
if not encrypted_user:
return encrypted_user
try:
return dp.decrypt_text(encrypted_user)
except Exception as e:
print(f"Error decrypting user data: {e}")
# Return fallback value or None to prevent downstream crashes
return "[Decryption Failed]"
def decrypt_item_user_data(item):
"""
Decrypts encrypted user fields within an inventory item document in-place.
Args:
item (dict): MongoDB document representing an item.
Returns:
dict: The item with decrypted user fields.
"""
if not item:
return item
# 1. Decrypt top-level 'User' field if present
if 'User' in item and item['User']:
item['User'] = safe_decrypt_user(item['User'])
# 2. Decrypt nested 'user' field in 'NextAppointment' if present
if 'NextAppointment' in item and isinstance(item['NextAppointment'], dict):
if 'user' in item['NextAppointment']:
item['NextAppointment']['user'] = safe_decrypt_user(item['NextAppointment']['user'])
return item
def _to_object_id(id_str):
"""Safely convert a string to ObjectId."""
try:
return ObjectId(id_str)
except (InvalidId, TypeError):
return None
LIBRARY_ITEM_TYPES = ('book', 'cd', 'dvd', 'other', 'schoolbook', 'Buch', 'Schulbuch', 'schulbuch') LIBRARY_ITEM_TYPES = ('book', 'cd', 'dvd', 'other', 'schoolbook', 'Buch', 'Schulbuch', 'schulbuch')
@@ -52,7 +102,7 @@ def add_item(name, ort, beschreibung, images=None, filter=None, filter2=None, fi
isbn=None, item_type='general', library_category=None, is_library=False): isbn=None, item_type='general', library_category=None, is_library=False):
""" """
Add a new item to the inventory. Add a new item to the inventory.
Args: Args:
name (str): Name of the item name (str): Name of the item
ort (str): Location of the item ort (str): Location of the item
@@ -73,7 +123,7 @@ def add_item(name, ort, beschreibung, images=None, filter=None, filter2=None, fi
isbn (str, optional): ISBN for books or media items isbn (str, optional): ISBN for books or media items
item_type (str, optional): Type of the item (e.g., 'general', 'book', 'cd') item_type (str, optional): Type of the item (e.g., 'general', 'book', 'cd')
library_category (str, optional): Library category for the item library_category (str, optional): Library category for the item
Returns: Returns:
ObjectId: ID of the new item or None if failed ObjectId: ID of the new item or None if failed
""" """
@@ -102,7 +152,7 @@ def add_item(name, ort, beschreibung, images=None, filter=None, filter2=None, fi
'ISBN': isbn, 'ISBN': isbn,
'library_category': library_category, 'library_category': library_category,
'ItemType': item_type, 'ItemType': item_type,
'is_library': is_library, 'is_library': is_library,
'SeriesGroupId': series_group_id, 'SeriesGroupId': series_group_id,
'SeriesCount': series_count, 'SeriesCount': series_count,
'SeriesPosition': series_position, 'SeriesPosition': series_position,
@@ -124,10 +174,10 @@ def add_item(name, ort, beschreibung, images=None, filter=None, filter2=None, fi
def remove_item(id): def remove_item(id):
""" """
Soft-delete an item from the inventory. Soft-delete an item from the inventory.
Args: Args:
id (str): ID of the item to remove id (str): ID of the item to remove
Returns: Returns:
bool: True if successful, False otherwise bool: True if successful, False otherwise
""" """
@@ -199,21 +249,19 @@ def get_group_item_ids(id):
return [] return []
def update_item(id, name, ort, beschreibung, images, verfuegbar, filter1, filter2, filter3, def update_item(id, name, ort, beschreibung, images, verfuegbar, filter1, filter2, filter3,
ansch_jahr, ansch_kost, code_4, reservierbar, isbn=None, item_type='general'): ansch_jahr, ansch_kost, code_4, reservierbar, isbn=None, item_type='general'):
try: try:
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB] db = client[cfg.MONGODB_DB]
items = db['items'] items = db['items']
# 1. Altes Item laden, um SeriesGroupId zu bestimmen
old_item = items.find_one({'_id': ObjectId(id)}) old_item = items.find_one({'_id': ObjectId(id)})
if not old_item: if not old_item:
return False return False
series_group_id = old_item.get('SeriesGroupId') series_group_id = old_item.get('SeriesGroupId')
# 2. Shared Data: Daten, die für ALLE in der Gruppe gleich sind
shared_update = { shared_update = {
'Name': name, 'Name': name,
'Ort': ort, 'Ort': ort,
@@ -227,18 +275,15 @@ def update_item(id, name, ort, beschreibung, images, verfuegbar, filter1, filter
'Reservierbar': reservierbar, 'Reservierbar': reservierbar,
'ISBN': isbn, 'ISBN': isbn,
'ItemType': item_type, 'ItemType': item_type,
'Verfuegbar': verfuegbar, # Wir behalten den Status bei 'Verfuegbar': verfuegbar,
'LastUpdated': datetime.datetime.now() 'LastUpdated': datetime.datetime.now()
} }
# 3. Spezifische Daten: Was NICHT synchronisiert wird
specific_update = shared_update.copy() specific_update = shared_update.copy()
specific_update['Code_4'] = code_4 specific_update['Code_4'] = code_4
# 4. Das aktuelle Item updaten
items.update_one({'_id': ObjectId(id)}, {'$set': specific_update}) items.update_one({'_id': ObjectId(id)}, {'$set': specific_update})
# 5. Alle anderen Gruppen-Mitglieder synchronisieren
if series_group_id: if series_group_id:
items.update_many( items.update_many(
{ {
@@ -257,12 +302,12 @@ def update_item(id, name, ort, beschreibung, images, verfuegbar, filter1, filter
def update_item_status(id, verfuegbar, user=None): def update_item_status(id, verfuegbar, user=None):
""" """
Update the availability status of an inventory item. Update the availability status of an inventory item.
Args: Args:
id (str): ID of the item to update id (str): ID of the item to update
verfuegbar (bool): New availability status verfuegbar (bool): New availability status
user (str, optional): Username of person who borrowed the item user (str, optional): Username of person who borrowed the item
Returns: Returns:
bool: True if successful, False otherwise bool: True if successful, False otherwise
""" """
@@ -279,7 +324,7 @@ def update_item_status(id, verfuegbar, user=None):
update_query = {'$set': update_data} update_query = {'$set': update_data}
if user is not None: if user is not None:
update_data['User'] = user update_data['User'] = dp.encrypt_text(user)
elif verfuegbar: elif verfuegbar:
# If item is being marked as available, clear the user field # If item is being marked as available, clear the user field
update_query['$unset'] = {'User': ""} update_query['$unset'] = {'User': ""}
@@ -299,11 +344,11 @@ def update_item_status(id, verfuegbar, user=None):
def update_item_exemplare_status(id, exemplare_status): def update_item_exemplare_status(id, exemplare_status):
""" """
Update the exemplar status of an inventory item. Update the exemplar status of an inventory item.
Args: Args:
id (str): ID of the item to update id (str): ID of the item to update
exemplare_status (list): List of status objects for each exemplar exemplare_status (list): List of status objects for each exemplar
Returns: Returns:
bool: True if successful, False otherwise bool: True if successful, False otherwise
""" """
@@ -332,32 +377,32 @@ def update_item_exemplare_status(id, exemplare_status):
def is_code_unique(code_4, exclude_id=None): def is_code_unique(code_4, exclude_id=None):
""" """
Check if a given code is unique (not used by any other item). Check if a given code is unique (not used by any other item).
Args: Args:
code_4 (str): The code to check code_4 (str): The code to check
exclude_id (str, optional): ID of item to exclude from the check (for edit operations) exclude_id (str, optional): ID of item to exclude from the check (for edit operations)
Returns: Returns:
bool: True if code is unique, False if already in use bool: True if code is unique, False if already in use
""" """
if not code_4 or code_4.strip() == "": if not code_4 or code_4.strip() == "":
# Empty codes are not considered unique # Empty codes are not considered unique
return False return False
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB] db = client[cfg.MONGODB_DB]
items = db['items'] items = db['items']
# Build query to find items with this code # Build query to find items with this code
query = {'Code_4': code_4, 'Deleted': {'$ne': True}} query = {'Code_4': code_4, 'Deleted': {'$ne': True}}
# If we're editing an item, exclude it from the uniqueness check # If we're editing an item, exclude it from the uniqueness check
if exclude_id: if exclude_id:
query['_id'] = {'$ne': ObjectId(exclude_id)} query['_id'] = {'$ne': ObjectId(exclude_id)}
# Check if any items with this code exist # Check if any items with this code exist
count = items.count_documents(query) count = items.count_documents(query)
client.close() client.close()
return count == 0 return count == 0
@@ -367,7 +412,7 @@ def is_code_unique(code_4, exclude_id=None):
def get_items(): def get_items():
""" """
Retrieve all inventory items. Retrieve all inventory items.
Returns: Returns:
list: List of all inventory item documents with string IDs list: List of all inventory item documents with string IDs
""" """
@@ -390,7 +435,7 @@ def get_items():
def get_available_items(): def get_available_items():
""" """
Retrieve all available inventory items. Retrieve all available inventory items.
Returns: Returns:
list: List of available inventory item documents with string IDs list: List of available inventory item documents with string IDs
""" """
@@ -413,7 +458,7 @@ def get_available_items():
def get_borrowed_items(): def get_borrowed_items():
""" """
Retrieve all currently borrowed inventory items. Retrieve all currently borrowed inventory items.
Returns: Returns:
list: List of borrowed inventory item documents with string IDs list: List of borrowed inventory item documents with string IDs
""" """
@@ -432,36 +477,36 @@ def get_borrowed_items():
print(f"Error retrieving borrowed items: {e}") print(f"Error retrieving borrowed items: {e}")
return [] return []
def get_item(id, decrypt=True):
"""
Retrieve an inventory item by ID, with optional decryption.
"""
item_id = _to_object_id(id)
if not item_id:
return None
def get_item(id):
"""
Retrieve a specific inventory item by its ID.
Args:
id (str): ID of the item to retrieve
Returns:
dict: The inventory item document or None if not found
"""
try: try:
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB] db = client[cfg.MONGODB_DB]
items = db['items'] items = db['items']
item = items.find_one(_active_record_query({'_id': ObjectId(id)})) query = _active_record_query({'_id': item_id})
client.close() item = items.find_one(query)
if item:
item['_id'] = str(item['_id'])
if decrypt:
decrypt_item_user_data(item)
return item return item
except Exception as e: except Exception as e:
print(f"Error retrieving item: {e}") print(f"Error retrieving item {id}: {e}")
return None return None
def get_item_by_name(name): def get_item_by_name(name):
""" """
Retrieve a specific inventory item by its name. Retrieve a specific inventory item by its name.
Args: Args:
name (str): Name of the item to retrieve name (str): Name of the item to retrieve
Returns: Returns:
dict: The inventory item document or None if not found dict: The inventory item document or None if not found
""" """
@@ -480,10 +525,10 @@ def get_item_by_name(name):
def get_items_by_filter(filter_value): def get_items_by_filter(filter_value):
""" """
Retrieve inventory items matching a specific filter/category. Retrieve inventory items matching a specific filter/category.
Args: Args:
filter_value (str): Filter value to search for filter_value (str): Filter value to search for
Returns: Returns:
list: List of items matching the filter in primary, secondary, or tertiary category list: List of items matching the filter in primary, secondary, or tertiary category
""" """
@@ -491,7 +536,7 @@ def get_items_by_filter(filter_value):
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB] db = client[cfg.MONGODB_DB]
items = db['items'] items = db['items']
# Use $or to find matches in any filter field # Use $or to find matches in any filter field
query = _active_record_query(_non_library_query({ query = _active_record_query(_non_library_query({
'$or': [ '$or': [
@@ -500,14 +545,14 @@ def get_items_by_filter(filter_value):
{'Filter3': filter_value} {'Filter3': filter_value}
] ]
})) }))
results = list(items.find(query)) results = list(items.find(query))
client.close() client.close()
# Convert ObjectId to string # Convert ObjectId to string
for item in results: for item in results:
item['_id'] = str(item['_id']) item['_id'] = str(item['_id'])
return results return results
except Exception as e: except Exception as e:
print(f"Error retrieving items by filter: {e}") print(f"Error retrieving items by filter: {e}")
@@ -517,7 +562,7 @@ def get_items_by_filter(filter_value):
def get_filters(): def get_filters():
""" """
Retrieve all unique filter/category values from the inventory. Retrieve all unique filter/category values from the inventory.
Returns: Returns:
list: Combined list of all primary, secondary and tertiary filter values list: Combined list of all primary, secondary and tertiary filter values
""" """
@@ -529,16 +574,16 @@ def get_filters():
filters = items.distinct('Filter', non_library) filters = items.distinct('Filter', non_library)
filters2 = items.distinct('Filter2', non_library) filters2 = items.distinct('Filter2', non_library)
filters3 = items.distinct('Filter3', non_library) filters3 = items.distinct('Filter3', non_library)
# Combine filters and remove None/empty values # Combine filters and remove None/empty values
all_filters = [f for f in filters + filters2 + filters3 if f] all_filters = [f for f in filters + filters2 + filters3 if f]
# Remove duplicates while preserving order # Remove duplicates while preserving order
unique_filters = [] unique_filters = []
for f in all_filters: for f in all_filters:
if f not in unique_filters: if f not in unique_filters:
unique_filters.append(f) unique_filters.append(f)
client.close() client.close()
return unique_filters return unique_filters
except Exception as e: except Exception as e:
@@ -549,7 +594,7 @@ def get_filters():
def get_primary_filters(): def get_primary_filters():
""" """
Retrieve all unique primary filter values. Retrieve all unique primary filter values.
Returns: Returns:
list: List of all primary filter values list: List of all primary filter values
""" """
@@ -559,7 +604,7 @@ def get_primary_filters():
items = db['items'] items = db['items']
filters = [f for f in items.distinct('Filter', _active_record_query(_non_library_query())) if f] filters = [f for f in items.distinct('Filter', _active_record_query(_non_library_query())) if f]
client.close() client.close()
# Add predefined values # Add predefined values
predefined = get_predefined_filter_values(1) predefined = get_predefined_filter_values(1)
return sorted(list(set(filters + predefined))) return sorted(list(set(filters + predefined)))
@@ -571,7 +616,7 @@ def get_primary_filters():
def get_secondary_filters(): def get_secondary_filters():
""" """
Retrieve all unique secondary filter values. Retrieve all unique secondary filter values.
Returns: Returns:
list: List of all secondary filter values list: List of all secondary filter values
""" """
@@ -581,7 +626,7 @@ def get_secondary_filters():
items = db['items'] items = db['items']
filters = [f for f in items.distinct('Filter2', _active_record_query(_non_library_query())) if f] filters = [f for f in items.distinct('Filter2', _active_record_query(_non_library_query())) if f]
client.close() client.close()
# Add predefined values # Add predefined values
predefined = get_predefined_filter_values(2) predefined = get_predefined_filter_values(2)
return sorted(list(set(filters + predefined))) return sorted(list(set(filters + predefined)))
@@ -593,7 +638,7 @@ def get_secondary_filters():
def get_tertiary_filters(): def get_tertiary_filters():
""" """
Retrieve all unique tertiary filter values. Retrieve all unique tertiary filter values.
Returns: Returns:
list: List of all tertiary filter values list: List of all tertiary filter values
""" """
@@ -603,7 +648,7 @@ def get_tertiary_filters():
items = db['items'] items = db['items']
filters = [f for f in items.distinct('Filter3', _active_record_query(_non_library_query())) if f] filters = [f for f in items.distinct('Filter3', _active_record_query(_non_library_query())) if f]
client.close() client.close()
# Add predefined values # Add predefined values
predefined = get_predefined_filter_values(3) predefined = get_predefined_filter_values(3)
return sorted(list(set(filters + predefined))) return sorted(list(set(filters + predefined)))
@@ -615,10 +660,10 @@ def get_tertiary_filters():
def get_item_by_code_4(code_4): def get_item_by_code_4(code_4):
""" """
Retrieve inventory items matching a specific 4-digit code. Retrieve inventory items matching a specific 4-digit code.
Args: Args:
code_4 (str): 4-digit code to search for code_4 (str): 4-digit code to search for
Returns: Returns:
list: List of items matching the code list: List of items matching the code
""" """
@@ -627,11 +672,11 @@ def get_item_by_code_4(code_4):
db = client[cfg.MONGODB_DB] db = client[cfg.MONGODB_DB]
items = db['items'] items = db['items']
results = list(items.find(_active_record_query(_non_library_query({"Code_4": code_4})))) results = list(items.find(_active_record_query(_non_library_query({"Code_4": code_4}))))
# Convert ObjectId to string # Convert ObjectId to string
for item in results: for item in results:
item['_id'] = str(item['_id']) item['_id'] = str(item['_id'])
client.close() client.close()
return results return results
except Exception as e: except Exception as e:
@@ -645,10 +690,10 @@ def unstuck_item(id):
""" """
Remove all borrowing records for a specific item to reset its status. Remove all borrowing records for a specific item to reset its status.
Used to fix problematic or stuck items. Used to fix problematic or stuck items.
Args: Args:
id (str): ID of the item to unstick id (str): ID of the item to unstick
Returns: Returns:
bool: True if successful, False otherwise bool: True if successful, False otherwise
""" """
@@ -664,7 +709,7 @@ def unstuck_item(id):
'LastUpdated': datetime.datetime.now() 'LastUpdated': datetime.datetime.now()
}} }}
) )
# Also reset the item status # Also reset the item status
items = db['items'] items = db['items']
items.update_one( items.update_one(
@@ -677,7 +722,7 @@ def unstuck_item(id):
'$unset': {'User': ""} '$unset': {'User': ""}
} }
) )
client.close() client.close()
return True return True
except Exception as e: except Exception as e:
@@ -688,24 +733,24 @@ def unstuck_item(id):
def get_predefined_filter_values(filter_num): def get_predefined_filter_values(filter_num):
""" """
Get predefined values for a specific filter. Get predefined values for a specific filter.
Args: Args:
filter_num (int): Filter number (1 for Unterrichtsfach, 2 for Jahrgangsstufe) filter_num (int): Filter number (1 for Unterrichtsfach, 2 for Jahrgangsstufe)
Returns: Returns:
list: List of predefined filter values list: List of predefined filter values
""" """
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB] db = client[cfg.MONGODB_DB]
# Use a dedicated collection for filter presets # Use a dedicated collection for filter presets
filter_presets = db['filter_presets'] filter_presets = db['filter_presets']
# Find the document for the specified filter # Find the document for the specified filter
filter_doc = filter_presets.find_one({'filter_num': filter_num}) filter_doc = filter_presets.find_one({'filter_num': filter_num})
client.close() client.close()
if filter_doc and 'values' in filter_doc: if filter_doc and 'values' in filter_doc:
# Sort values alphabetically # Sort values alphabetically
return sorted(filter_doc['values']) return sorted(filter_doc['values'])
@@ -725,60 +770,60 @@ def get_predefined_filter_values(filter_num):
def add_predefined_filter_value(filter_num, value): def add_predefined_filter_value(filter_num, value):
""" """
Add a new predefined value to a filter. Add a new predefined value to a filter.
Args: Args:
filter_num (int): Filter number (1 for Unterrichtsfach, 2 for Jahrgangsstufe) filter_num (int): Filter number (1 for Unterrichtsfach, 2 for Jahrgangsstufe)
value (str): Value to add value (str): Value to add
Returns: Returns:
bool: True if value was added, False if it already existed bool: True if value was added, False if it already existed
""" """
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB] db = client[cfg.MONGODB_DB]
filter_presets = db['filter_presets'] filter_presets = db['filter_presets']
# Check if value already exists # Check if value already exists
filter_doc = filter_presets.find_one({ filter_doc = filter_presets.find_one({
'filter_num': filter_num, 'filter_num': filter_num,
'values': value 'values': value
}) })
if filter_doc: if filter_doc:
# Value already exists # Value already exists
client.close() client.close()
return False return False
# Add the value to the filter # Add the value to the filter
result = filter_presets.update_one( result = filter_presets.update_one(
{'filter_num': filter_num}, {'filter_num': filter_num},
{'$push': {'values': value}}, {'$push': {'values': value}},
upsert=True upsert=True
) )
client.close() client.close()
return result.modified_count > 0 or result.upserted_id is not None return result.modified_count > 0 or result.upserted_id is not None
def remove_predefined_filter_value(filter_num, value): def remove_predefined_filter_value(filter_num, value):
""" """
Remove a predefined value from a filter. Remove a predefined value from a filter.
Args: Args:
filter_num (int): Filter number (1 for Unterrichtsfach, 2 for Jahrgangsstufe) filter_num (int): Filter number (1 for Unterrichtsfach, 2 for Jahrgangsstufe)
value (str): Value to remove value (str): Value to remove
Returns: Returns:
bool: True if value was removed, False otherwise bool: True if value was removed, False otherwise
""" """
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB] db = client[cfg.MONGODB_DB]
filter_presets = db['filter_presets'] filter_presets = db['filter_presets']
# Remove the value from the filter # Remove the value from the filter
result = filter_presets.update_one( result = filter_presets.update_one(
{'filter_num': filter_num}, {'filter_num': filter_num},
{'$pull': {'values': value}} {'$pull': {'values': value}}
) )
client.close() client.close()
return result.modified_count > 0 return result.modified_count > 0
@@ -786,45 +831,45 @@ def remove_predefined_filter_value(filter_num, value):
def edit_predefined_filter_value(filter_num, old_value, new_value): def edit_predefined_filter_value(filter_num, old_value, new_value):
""" """
Edit a predefined value from a filter and update all matching items. Edit a predefined value from a filter and update all matching items.
Args: Args:
filter_num (int): Filter number (1 for Unterrichtsfach, 2 for Jahrgangsstufe) filter_num (int): Filter number (1 for Unterrichtsfach, 2 for Jahrgangsstufe)
old_value (str): Value to replace old_value (str): Value to replace
new_value (str): New value new_value (str): New value
Returns: Returns:
bool: True if value was updated, False otherwise bool: True if value was updated, False otherwise
""" """
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB] db = client[cfg.MONGODB_DB]
filter_presets = db['filter_presets'] filter_presets = db['filter_presets']
# Check if the new value already exists # Check if the new value already exists
existing = filter_presets.find_one({ existing = filter_presets.find_one({
'filter_num': filter_num, 'filter_num': filter_num,
'values': new_value 'values': new_value
}) })
if existing and old_value != new_value: if existing and old_value != new_value:
client.close() client.close()
return False return False
# Update the value in the filter # Update the value in the filter
result = filter_presets.update_one( result = filter_presets.update_one(
{'filter_num': filter_num, 'values': old_value}, {'filter_num': filter_num, 'values': old_value},
{'$set': {'values.$': new_value}} {'$set': {'values.$': new_value}}
) )
if result.modified_count > 0: if result.modified_count > 0:
items = db['items'] items = db['items']
filter_field = 'Filter' if filter_num == 1 else f'Filter{filter_num}' filter_field = 'Filter' if filter_num == 1 else f'Filter{filter_num}'
# Also update all items that use this filter # Also update all items that use this filter
items.update_many( items.update_many(
{filter_field: old_value}, {filter_field: old_value},
{'$set': {filter_field: new_value}} {'$set': {filter_field: new_value}}
) )
client.close() client.close()
return result.modified_count > 0 return result.modified_count > 0
@@ -862,22 +907,22 @@ def set_filter_name(filter_num, name):
def get_predefined_locations(): def get_predefined_locations():
""" """
Get list of all predefined locations/placement options. Get list of all predefined locations/placement options.
Returns: Returns:
list: List of predefined location strings list: List of predefined location strings
""" """
try: try:
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB] db = client[cfg.MONGODB_DB]
# Check if settings collection exists, create if not # Check if settings collection exists, create if not
if 'settings' not in db.list_collection_names(): if 'settings' not in db.list_collection_names():
db.create_collection('settings') db.create_collection('settings')
# Get settings document or create if it doesn't exist # Get settings document or create if it doesn't exist
settings_collection = db['settings'] settings_collection = db['settings']
location_settings = settings_collection.find_one({'setting_type': 'predefined_locations'}) location_settings = settings_collection.find_one({'setting_type': 'predefined_locations'})
if not location_settings: if not location_settings:
# Create default settings document if it doesn't exist # Create default settings document if it doesn't exist
settings_collection.insert_one({ settings_collection.insert_one({
@@ -885,12 +930,12 @@ def get_predefined_locations():
'locations': [] 'locations': []
}) })
return [] return []
# Return the predefined locations # Return the predefined locations
locations = location_settings.get('locations', []) locations = location_settings.get('locations', [])
client.close() client.close()
return sorted(locations) return sorted(locations)
except Exception as e: except Exception as e:
print(f"Error getting predefined locations: {str(e)}") print(f"Error getting predefined locations: {str(e)}")
return [] return []
@@ -899,28 +944,28 @@ def get_predefined_locations():
def add_predefined_location(location): def add_predefined_location(location):
""" """
Add a new predefined location. Add a new predefined location.
Args: Args:
location (str): Location to add location (str): Location to add
Returns: Returns:
bool: True if added successfully, False if already exists bool: True if added successfully, False if already exists
""" """
if not location or not isinstance(location, str): if not location or not isinstance(location, str):
return False return False
location = location.strip() location = location.strip()
if not location: if not location:
return False return False
try: try:
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB] db = client[cfg.MONGODB_DB]
settings_collection = db['settings'] settings_collection = db['settings']
# Check if settings document exists, create if not # Check if settings document exists, create if not
location_settings = settings_collection.find_one({'setting_type': 'predefined_locations'}) location_settings = settings_collection.find_one({'setting_type': 'predefined_locations'})
if not location_settings: if not location_settings:
# Create with the new location # Create with the new location
settings_collection.insert_one({ settings_collection.insert_one({
@@ -929,22 +974,22 @@ def add_predefined_location(location):
}) })
client.close() client.close()
return True return True
# Check if location already exists (case-insensitive) # Check if location already exists (case-insensitive)
current_locations = location_settings.get('locations', []) current_locations = location_settings.get('locations', [])
if any(loc.lower() == location.lower() for loc in current_locations): if any(loc.lower() == location.lower() for loc in current_locations):
client.close() client.close()
return False return False
# Add the new location # Add the new location
settings_collection.update_one( settings_collection.update_one(
{'setting_type': 'predefined_locations'}, {'setting_type': 'predefined_locations'},
{'$push': {'locations': location}} {'$push': {'locations': location}}
) )
client.close() client.close()
return True return True
except Exception as e: except Exception as e:
print(f"Error adding predefined location: {str(e)}") print(f"Error adding predefined location: {str(e)}")
return False return False
@@ -953,29 +998,29 @@ def add_predefined_location(location):
def remove_predefined_location(location): def remove_predefined_location(location):
""" """
Remove a predefined location. Remove a predefined location.
Args: Args:
location (str): Location to remove location (str): Location to remove
Returns: Returns:
bool: True if removed successfully bool: True if removed successfully
""" """
if not location: if not location:
return False return False
try: try:
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB] db = client[cfg.MONGODB_DB]
settings_collection = db['settings'] settings_collection = db['settings']
result = settings_collection.update_one( result = settings_collection.update_one(
{'setting_type': 'predefined_locations'}, {'setting_type': 'predefined_locations'},
{'$pull': {'locations': location}} {'$pull': {'locations': location}}
) )
client.close() client.close()
return result.modified_count > 0 return result.modified_count > 0
except Exception as e: except Exception as e:
print(f"Error removing predefined location: {str(e)}") print(f"Error removing predefined location: {str(e)}")
return False return False
@@ -984,17 +1029,12 @@ def remove_predefined_location(location):
def update_item_next_appointment(item_id, appointment_data): def update_item_next_appointment(item_id, appointment_data):
""" """
Update an item with information about its next scheduled appointment. Update an item with information about its next scheduled appointment.
Args: Args:
item_id (str): ID of the item to update item_id (str): ID of the item
appointment_data (dict): Appointment information containing: appointment_data (dict or None): Dictionary containing appointment details
- date: Date of the appointment (e.g., user, start_time, end_time) or None to clear it.
- start_period: Start period number
- end_period: End period number
- user: Username who scheduled the appointment
- notes: Optional notes
- appointment_id: ID of the appointment booking
Returns: Returns:
bool: True if successful, False otherwise bool: True if successful, False otherwise
""" """
@@ -1002,35 +1042,33 @@ def update_item_next_appointment(item_id, appointment_data):
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB] db = client[cfg.MONGODB_DB]
items = db['items'] items = db['items']
# Format the appointment data for storage # If clearing the appointment
# Ensure date is a datetime object for MongoDB storage if appointment_data is None:
appointment_date = appointment_data['date'] update_query = {
if isinstance(appointment_date, datetime.date) and not isinstance(appointment_date, datetime.datetime): '$unset': {'NextAppointment': ""},
# Convert date to datetime for MongoDB compatibility '$set': {'LastUpdated': datetime.datetime.now()}
appointment_date = datetime.datetime.combine(appointment_date, datetime.time()) }
else:
next_appointment = { # Create a copy so we don't mutate the original dictionary passed in
'date': appointment_date, data_to_save = appointment_data.copy()
'end_date': appointment_data.get('end_date', appointment_date),
'start_period': appointment_data['start_period'], # Encrypt the user field if it exists to match the decryption logic at the top
'end_period': appointment_data['end_period'], if 'user' in data_to_save and data_to_save['user']:
'user': appointment_data['user'], data_to_save['user'] = dp.encrypt_text(data_to_save['user'])
'notes': appointment_data.get('notes', ''),
'appointment_id': appointment_data['appointment_id'], update_query = {
'scheduled_at': datetime.datetime.now() '$set': {
} 'NextAppointment': data_to_save,
'LastUpdated': datetime.datetime.now()
update_data = { }
'NextAppointment': next_appointment, }
'LastUpdated': datetime.datetime.now()
}
result = items.update_one( result = items.update_one(
{'_id': ObjectId(item_id)}, {'_id': ObjectId(item_id)},
{'$set': update_data} update_query
) )
client.close() client.close()
return result.modified_count > 0 return result.modified_count > 0
except Exception as e: except Exception as e:
@@ -1041,10 +1079,10 @@ def update_item_next_appointment(item_id, appointment_data):
def clear_item_next_appointment(item_id): def clear_item_next_appointment(item_id):
""" """
Clear the next appointment information from an item. Clear the next appointment information from an item.
Args: Args:
item_id (str): ID of the item to update item_id (str): ID of the item to update
Returns: Returns:
bool: True if successful, False otherwise bool: True if successful, False otherwise
""" """
@@ -1052,12 +1090,12 @@ def clear_item_next_appointment(item_id):
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB] db = client[cfg.MONGODB_DB]
items = db['items'] items = db['items']
result = items.update_one( result = items.update_one(
{'_id': ObjectId(item_id)}, {'_id': ObjectId(item_id)},
{'$unset': {'NextAppointment': ""}, '$set': {'LastUpdated': datetime.datetime.now()}} {'$unset': {'NextAppointment': ""}, '$set': {'LastUpdated': datetime.datetime.now()}}
) )
client.close() client.close()
return result.modified_count > 0 return result.modified_count > 0
except Exception as e: except Exception as e:
@@ -1068,7 +1106,7 @@ def clear_item_next_appointment(item_id):
def get_items_with_appointments(): def get_items_with_appointments():
""" """
Retrieve all items that have scheduled appointments. Retrieve all items that have scheduled appointments.
Returns: Returns:
list: List of items with NextAppointment field list: List of items with NextAppointment field
""" """
@@ -1076,7 +1114,7 @@ def get_items_with_appointments():
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB] db = client[cfg.MONGODB_DB]
items = db['items'] items = db['items']
items_return = items.find({'NextAppointment': {'$exists': True}, 'Deleted': {'$ne': True}}) items_return = items.find({'NextAppointment': {'$exists': True}, 'Deleted': {'$ne': True}})
items_list = [] items_list = []
for item in items_return: for item in items_return:
@@ -1088,31 +1126,25 @@ def get_items_with_appointments():
print(f"Error retrieving items with appointments: {e}") print(f"Error retrieving items with appointments: {e}")
return [] return []
def get_current_status(item_id): def get_current_status(item_id, decrypt=True):
""" """
Retrieve the current status of an item, including availability and user. Retrieve the current status of an item, decrypting the user field if present.
Args:
item_id (str): ID of the item to check
Returns:
dict: Current status of the item or None if not found
""" """
oid = _to_object_id(item_id)
if not oid:
return None
try: try:
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = client[cfg.MONGODB_DB] db = client[cfg.MONGODB_DB]
items = db['items'] items = db['items']
item = items.find_one({'_id': oid}, {'Verfuegbar': 1, 'User': 1})
item = items.find_one({'_id': ObjectId(item_id)}, {'Verfuegbar': 1, 'User': 1})
if item: if item:
# Convert ObjectId to string for consistency
item['_id'] = str(item['_id']) item['_id'] = str(item['_id'])
client.close() if decrypt:
decrypt_item_user_data(item)
return item return item
else: return None
client.close()
return None
except Exception as e: except Exception as e:
print(f"Error retrieving current status: {e}") print(f"Error retrieving current status for item {item_id}: {e}")
return None return None
+5 -5
View File
@@ -319,15 +319,15 @@ THUMBNAIL_FOLDER = _get(_conf, ['upload', 'thumbnail_folder'], DEFAULTS['upload'
PREVIEW_FOLDER = _get(_conf, ['upload', 'preview_folder'], DEFAULTS['upload']['preview_folder']) PREVIEW_FOLDER = _get(_conf, ['upload', 'preview_folder'], DEFAULTS['upload']['preview_folder'])
QR_CODE_FOLDER = _get(_conf, ['upload', 'qrcode_folder'], DEFAULTS['upload']['qrcode_folder']) QR_CODE_FOLDER = _get(_conf, ['upload', 'qrcode_folder'], DEFAULTS['upload']['qrcode_folder'])
# Normalize to absolute paths to avoid cwd issues # This guarantees exact matching with Docker volume mounts
if not os.path.isabs(UPLOAD_FOLDER): if not os.path.isabs(UPLOAD_FOLDER):
UPLOAD_FOLDER = os.path.join(BASE_DIR, os.path.relpath(UPLOAD_FOLDER, BASE_DIR)) UPLOAD_FOLDER = os.path.abspath(os.path.join(BASE_DIR, "uploads"))
if not os.path.isabs(THUMBNAIL_FOLDER): if not os.path.isabs(THUMBNAIL_FOLDER):
THUMBNAIL_FOLDER = os.path.join(BASE_DIR, os.path.relpath(THUMBNAIL_FOLDER, BASE_DIR)) THUMBNAIL_FOLDER = os.path.abspath(os.path.join(BASE_DIR, "thumbnails"))
if not os.path.isabs(PREVIEW_FOLDER): if not os.path.isabs(PREVIEW_FOLDER):
PREVIEW_FOLDER = os.path.join(BASE_DIR, os.path.relpath(PREVIEW_FOLDER, BASE_DIR)) PREVIEW_FOLDER = os.path.abspath(os.path.join(BASE_DIR, "previews"))
if not os.path.isabs(QR_CODE_FOLDER): if not os.path.isabs(QR_CODE_FOLDER):
QR_CODE_FOLDER = os.path.join(BASE_DIR, os.path.relpath(QR_CODE_FOLDER, BASE_DIR)) QR_CODE_FOLDER = os.path.abspath(os.path.join(BASE_DIR, "QRCodes"))
MAX_UPLOAD_MB = _get(_conf, ['upload', 'max_size_mb'], DEFAULTS['upload']['max_size_mb']) MAX_UPLOAD_MB = _get(_conf, ['upload', 'max_size_mb'], DEFAULTS['upload']['max_size_mb'])
IMAGE_MAX_UPLOAD_MB = _get(_conf, ['upload', 'image_max_size_mb'], DEFAULTS['upload']['image_max_size_mb']) IMAGE_MAX_UPLOAD_MB = _get(_conf, ['upload', 'image_max_size_mb'], DEFAULTS['upload']['image_max_size_mb'])
VIDEO_MAX_UPLOAD_MB = _get(_conf, ['upload', 'video_max_size_mb'], DEFAULTS['upload']['video_max_size_mb']) VIDEO_MAX_UPLOAD_MB = _get(_conf, ['upload', 'video_max_size_mb'], DEFAULTS['upload']['video_max_size_mb'])
+135 -252
View File
@@ -20,6 +20,7 @@ import string
from bson.objectid import ObjectId from bson.objectid import ObjectId
import Web.modules.database.settings as cfg import Web.modules.database.settings as cfg
from Web.modules.database.settings import MongoClient from Web.modules.database.settings import MongoClient
import Web.modules.inventarsystem.data_protection as dp
import hmac import hmac
import os import os
@@ -109,13 +110,6 @@ def build_username_from_name(first_name, last_name=''):
""" """
Build a deterministic username abbreviation from first and last name. Build a deterministic username abbreviation from first and last name.
Uses 3 letters from each name and stores it lowercase. Uses 3 letters from each name and stores it lowercase.
Args:
first_name (str): First name
last_name (str): Last name (optional)
Returns:
str: Generated username
""" """
alias = build_name_synonym(first_name, last_name) alias = build_name_synonym(first_name, last_name)
return alias.lower() return alias.lower()
@@ -324,7 +318,6 @@ def get_effective_permissions(username):
return build_default_permission_payload('full_access') return build_default_permission_payload('full_access')
preset_key = user.get('PermissionPreset') preset_key = user.get('PermissionPreset')
print(preset_key)
payload = build_default_permission_payload(preset_key) payload = build_default_permission_payload(preset_key)
payload['actions'] = _normalize_bool_map(user.get('ActionPermissions', {}), payload['actions']) payload['actions'] = _normalize_bool_map(user.get('ActionPermissions', {}), payload['actions'])
payload['pages'] = _normalize_bool_map(user.get('PagePermissions', {}), payload['pages']) payload['pages'] = _normalize_bool_map(user.get('PagePermissions', {}), payload['pages'])
@@ -352,10 +345,10 @@ def update_user_permissions(username, preset_key, action_permissions=None, page_
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = _get_tenant_db(client) db = _get_tenant_db(client)
users = db['users'] users = db['users']
result = users.update_one({'Username': username}, {'$set': update_data}) result = users.update_one({'Username': dp.encrypt_text(username)}, {'$set': update_data})
if result.matched_count == 0: if result.matched_count == 0:
result = users.update_one({'username': username}, {'$set': update_data}) result = users.update_one({'username': dp.encrypt_text(username)}, {'$set': update_data})
client.close() client.close()
return result.matched_count > 0 return result.matched_count > 0
@@ -367,7 +360,7 @@ def get_favorites(username):
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = _get_tenant_db(client) db = _get_tenant_db(client)
users = db['users'] users = db['users']
user = users.find_one({'Username': username}) or users.find_one({'username': username}) user = users.find_one({'Username': dp.encrypt_text(username)}) or users.find_one({'username': dp.encrypt_text(username)})
client.close() client.close()
if not user: if not user:
return [] return []
@@ -382,7 +375,7 @@ def add_favorite(username, item_id):
db = _get_tenant_db(client) db = _get_tenant_db(client)
users = db['users'] users = db['users']
users.update_one( users.update_one(
{'$or': [{'Username': username}, {'username': username}]}, {'$or': [{'Username': dp.encrypt_text(username)}, {'username': dp.encrypt_text(username)}]},
{'$addToSet': {'favorites': ObjectId(item_id)}} {'$addToSet': {'favorites': ObjectId(item_id)}}
) )
client.close() client.close()
@@ -397,7 +390,7 @@ def remove_favorite(username, item_id):
db = _get_tenant_db(client) db = _get_tenant_db(client)
users = db['users'] users = db['users']
users.update_one( users.update_one(
{'$or': [{'Username': username}, {'username': username}]}, {'$or': [{'Username': dp.encrypt_text(username)}, {'username': dp.encrypt_text(username)}]},
{'$pull': {'favorites': ObjectId(item_id)}} {'$pull': {'favorites': ObjectId(item_id)}}
) )
client.close() client.close()
@@ -406,16 +399,9 @@ def remove_favorite(username, item_id):
return False return False
def check_password_strength(password): def check_password_strength(password):
""" """
Check if a password meets minimum security requirements. Check if a password meets minimum security requirements.
Args:
password (str): Password to check
Returns:
bool: True if password is strong enough, False otherwise
""" """
if password is None: if password is None:
return False return False
@@ -435,19 +421,15 @@ def check_password_strength(password):
def hashing(password, salt=None): def hashing(password, salt=None):
""" """
Hasht ein Passwort mit scrypt. Hasht ein Passwort mit scrypt.
- Wenn kein Salt übergeben wird, wird ein sicherer, zufälliger Salt generiert (für neue Passwörter).
- Format für neue Hashes: v1$<salt_hex>$<hash_hex>
""" """
password_bytes = password.encode('utf-8') # Explizit UTF-8 für Plattformunabhängigkeit password_bytes = password.encode('utf-8')
if salt is None: if salt is None:
# Neuer Benutzer / Passwortänderung -> Dynamischer Salt
random_salt = os.urandom(16) random_salt = os.urandom(16)
hashed = hashlib.scrypt(password_bytes, salt=random_salt, n=16384, r=8, p=1) hashed = hashlib.scrypt(password_bytes, salt=random_salt, n=16384, r=8, p=1)
return f"v1${random_salt.hex()}${hashed.hex()}" return f"v1${random_salt.hex()}${hashed.hex()}"
else: else:
# Bestehender Benutzer (wird zur Verifizierung aufgerufen)
hashed = hashlib.scrypt(password_bytes, salt=salt, n=16384, r=8, p=1) hashed = hashlib.scrypt(password_bytes, salt=salt, n=16384, r=8, p=1)
return hashed.hex() return hashed.hex()
@@ -455,25 +437,20 @@ def hashing(password, salt=None):
def verify_password(provided_password, stored_password_string): def verify_password(provided_password, stored_password_string):
""" """
Verifiziert ein Passwort gegen einen gespeicherten Hash-String. Verifiziert ein Passwort gegen einen gespeicherten Hash-String.
Unterstützt das alte Format (statischer Salt) und das neue Format (v1$...).
""" """
if not stored_password_string: if not stored_password_string:
return False return False
# Überprüfung für das neue, sichere Format
if stored_password_string.startswith("v1$"): if stored_password_string.startswith("v1$"):
try: try:
_, salt_hex, hash_hex = stored_password_string.split("$") _, salt_hex, hash_hex = stored_password_string.split("$")
salt_bytes = bytes.fromhex(salt_hex) salt_bytes = bytes.fromhex(salt_hex)
# Berechne den Hash des eingegebenen Passworts mit dem extrahierten Salt
calculated_hash = hashing(provided_password, salt=salt_bytes) calculated_hash = hashing(provided_password, salt=salt_bytes)
# Timing-Attack-sicherer Vergleich
return hmac.compare_digest(calculated_hash, hash_hex) return hmac.compare_digest(calculated_hash, hash_hex)
except (ValueError, TypeError): except (ValueError, TypeError):
logger.error("Ungültiges Hash-Format in der Datenbank entdeckt.") logger.error("Ungültiges Hash-Format in der Datenbank entdeckt.")
return False return False
else: else:
# Abwärtskompatibilität: Altes Format mit statischem Salt b'some_salt'
old_static_salt = b'some_salt' old_static_salt = b'some_salt'
calculated_hash = hashing(provided_password, salt=old_static_salt) calculated_hash = hashing(provided_password, salt=old_static_salt)
return hmac.compare_digest(calculated_hash, stored_password_string) return hmac.compare_digest(calculated_hash, stored_password_string)
@@ -494,22 +471,26 @@ def check_nm_pwd(username, password):
try: try:
db = client[db_name] db = client[db_name]
users = db['users'] users = db['users']
query = {'$or': [{'Username': username}, {'username': username}]} query = {'$or': [{'Username': dp.encrypt_text(username)}, {'username': dp.encrypt_text(username)}]}
user_record = users.find_one(query) user_record = users.find_one(query)
if user_record is None: if user_record is None:
logger.warning("Kein Benutzer für %r in DB %r gefunden.", username, db_name) query = {'$or': [{'Username': username}, {'username': username}]}
return None user_record_fallback = users.find_one(query)
if user_record_fallback is None:
logger.warning("Kein Benutzer für %r in DB %r gefunden.", dp.encrypt_text(username), db_name)
return None
else:
user_record = user_record_fallback
stored_password = user_record.get('Password') or user_record.get('password') stored_password = user_record.get('Password') or user_record.get('password')
if not verify_password(password, stored_password): if not verify_password(password, stored_password):
logger.warning("Falsches Passwort für Benutzer %r in DB %r.", username, db_name) logger.warning("Falsches Passwort für Benutzer %r in DB %r.", dp.encrypt_text(username), db_name)
return None return None
# Automatische Migration alter Hashes auf das neue Format if stored_password and not stored_password.startswith("v1$"):
if not stored_password.startswith("v1$"):
users.update_one({'_id': user_record['_id']}, {'$set': {'Password': hashing(password)}}) users.update_one({'_id': user_record['_id']}, {'$set': {'Password': hashing(password)}})
return user_record return user_record
@@ -531,40 +512,35 @@ def add_user(
): ):
""" """
Add a new user to the database. Add a new user to the database.
Args:
username (str): Username for the new user
password (str): Password for the new user
Returns:
bool: True if user was added successfully, False if password was too weak
""" """
if not check_password_strength(password):
return False
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
try: try:
db = _get_tenant_db(client) db = _get_tenant_db(client)
users = db['users'] users = db['users']
if not check_password_strength(password):
return False
permission_defaults = build_default_permission_payload(permission_preset) permission_defaults = build_default_permission_payload(permission_preset)
if isinstance(action_permissions, dict): if isinstance(action_permissions, dict):
for key, value in action_permissions.items(): for key, value in action_permissions.items():
permission_defaults['actions'][str(key)] = bool(value) permission_defaults['actions'][str(key)] = bool(value)
if isinstance(page_permissions, dict): if isinstance(page_permissions, dict):
for key, value in page_permissions.items(): for key, value in page_permissions.items():
permission_defaults['pages'][str(key)] = bool(value) permission_defaults['pages'][str(key)] = bool(value)
if permission_preset == "full_access": safe_name = name.strip() if name else ''
can_admin_preset_based = True safe_last_name = last_name.strip() if last_name else ''
else:
can_admin_preset_based = False
user_doc = { user_doc = {
'Username': username, 'Username': dp.encrypt_text(username),
'Password': hashing(password), 'Password': hashing(password),
'Admin': can_admin_preset_based, 'Admin': (permission_preset == "full_access"),
'active_ausleihung': None, 'active_ausleihung': None,
'name': name.strip() if name else '', 'name': dp.encrypt_text(safe_name) if safe_name else '',
'last_name': last_name.strip() if last_name else '', 'last_name': dp.encrypt_text(safe_last_name) if safe_last_name else '',
'IsStudent': bool(is_student), 'IsStudent': bool(is_student),
'PermissionPreset': permission_defaults['preset'], 'PermissionPreset': permission_defaults['preset'],
'ActionPermissions': permission_defaults['actions'], 'ActionPermissions': permission_defaults['actions'],
@@ -601,7 +577,7 @@ def student_card_exists(student_card_id):
def get_user_by_student_card(student_card_id): def get_user_by_student_card(student_card_id):
"""Return user by student card id or None.""" """Return user dict by student card id or None."""
normalized = normalize_student_card_id(student_card_id) normalized = normalize_student_card_id(student_card_id)
if not normalized: if not normalized:
return None return None
@@ -610,65 +586,44 @@ def get_user_by_student_card(student_card_id):
users = db['student_cards'] users = db['student_cards']
found_user = users.find_one({'SchülerName': normalized}) found_user = users.find_one({'SchülerName': normalized})
client.close() client.close()
# Do not call dp.decrypt_text() here because found_user is a MongoDB dictionary.
return found_user return found_user
def make_admin(username): def make_admin(username):
""" """Grant administrator privileges to a user."""
Grant administrator privileges to a user.
Args:
username (str): Username of the user to promote
Returns:
bool: True if user was promoted successfully
"""
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = _get_tenant_db(client) db = _get_tenant_db(client)
users = db['users'] users = db['users']
result = users.update_one({'Username': username}, {'$set': {'Admin': True}}) result = users.update_one({'Username': dp.encrypt_text(username)}, {'$set': {'Admin': True}})
if result.matched_count == 0: if result.matched_count == 0:
result = users.update_one({'username': username}, {'$set': {'Admin': True}}) result = users.update_one({'username': dp.encrypt_text(username)}, {'$set': {'Admin': True}})
client.close() client.close()
return result.matched_count > 0 return result.matched_count > 0
def remove_admin(username): def remove_admin(username):
""" """Remove administrator privileges from a user."""
Remove administrator privileges from a user.
Args:
username (str): Username of the user to demote
Returns:
bool: True if user was demoted successfully
"""
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = _get_tenant_db(client) db = _get_tenant_db(client)
users = db['users'] users = db['users']
result = users.update_one({'Username': username}, {'$set': {'Admin': False}}) result = users.update_one({'Username': dp.encrypt_text(username)}, {'$set': {'Admin': False}})
if result.matched_count == 0: if result.matched_count == 0:
result = users.update_one({'username': username}, {'$set': {'Admin': False}}) result = users.update_one({'username': dp.encrypt_text(username)}, {'$set': {'Admin': False}})
client.close() client.close()
return result.matched_count > 0 return result.matched_count > 0
def get_user(username): def get_user(username):
""" """Retrieve a specific user by username."""
Retrieve a specific user by username.
Args:
username (str): Username to search for
Returns:
dict: User document or None if not found
"""
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
try: try:
def find_in_db(database_name): def find_in_db(database_name):
db = client[database_name] db = client[database_name]
users = db['users'] users = db['users']
return users.find_one({'Username': username}) or users.find_one({'username': username}) return users.find_one({'Username': dp.encrypt_text(username)}) or users.find_one({'username': dp.encrypt_text(username)}) or users.find_one({'username': username}) or users.find_one({'Username': username})
# Try current tenant first when available
tenant_db, tenant_id = _resolve_request_tenant_db() tenant_db, tenant_id = _resolve_request_tenant_db()
if tenant_db: if tenant_db:
user = find_in_db(tenant_db) user = find_in_db(tenant_db)
@@ -681,7 +636,6 @@ def get_user(username):
) )
return None return None
# Fallback to default configured database
user = find_in_db(cfg.MONGODB_DB) user = find_in_db(cfg.MONGODB_DB)
if user: if user:
return user return user
@@ -692,147 +646,89 @@ def get_user(username):
def check_admin(username): def check_admin(username):
""" """Check if a user has administrator privileges."""
Check if a user has administrator privileges.
Args:
username (str): Username to check
Returns:
bool: True if user is an administrator, False otherwise
"""
user = get_user(username) user = get_user(username)
return bool(user and user.get('Admin', False)) return bool(user and user.get('Admin', False))
def update_active_ausleihung(username, id_item, ausleihung): def update_active_ausleihung(username, id_item, ausleihung):
""" """Update a user's active borrowing record."""
Update a user's active borrowing record.
Args:
username (str): Username of the user
id_item (str): ID of the borrowed item
ausleihung (str): ID of the borrowing record
Returns:
bool: True if successful
"""
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = _get_tenant_db(client) db = _get_tenant_db(client)
users = db['users'] users = db['users']
users.update_one({'Username': username}, {'$set': {'active_ausleihung': {'Item': id_item, 'Ausleihung': ausleihung}}})
result = users.update_one(
{'Username': dp.encrypt_text(username)},
{'$set': {'active_ausleihung': {'Item': id_item, 'Ausleihung': ausleihung}}}
)
if result.matched_count == 0:
users.update_one(
{'username': dp.encrypt_text(username)},
{'$set': {'active_ausleihung': {'Item': id_item, 'Ausleihung': ausleihung}}}
)
client.close() client.close()
return True return True
def get_active_ausleihung(username): def get_active_ausleihung(username):
""" """Get a user's active borrowing record."""
Get a user's active borrowing record.
Args:
username (str): Username of the user
Returns:
dict: Active borrowing information or None
"""
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = _get_tenant_db(client) db = _get_tenant_db(client)
users = db['users'] users = db['users']
user = users.find_one({'Username': username})
return user['active_ausleihung'] user = users.find_one({'Username': dp.encrypt_text(username)}) or users.find_one({'username': dp.encrypt_text(username)})
client.close()
if not user:
return None
return user.get('active_ausleihung')
def has_active_borrowing(username): def has_active_borrowing(username):
""" """Check if a user currently has an active borrowing."""
Check if a user currently has an active borrowing.
Args:
username (str): Username to check
Returns:
bool: True if user has an active borrowing, False otherwise
"""
try: try:
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = _get_tenant_db(client) db = _get_tenant_db(client)
users = db['users'] users = db['users']
user = users.find_one({'username': username}) user = users.find_one({'username': dp.encrypt_text(username)}) or users.find_one({'Username': dp.encrypt_text(username)})
if not user:
user = users.find_one({'Username': username})
if not user:
client.close()
return False
has_active = user.get('active_borrowing', False)
client.close() client.close()
return has_active
if not user:
return False
return user.get('active_borrowing', False)
except Exception as e: except Exception as e:
return False return False
def delete_user(username): def delete_user(username):
""" """Delete a user from the database."""
Delete a user from the database.
Administrative function for removing user accounts.
Args:
username (str): Username of the account to delete
Returns:
bool: True if user was deleted successfully, False otherwise
"""
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = _get_tenant_db(client) db = _get_tenant_db(client)
users = db['users'] users = db['users']
result = users.delete_one({'username': username})
client.close() result = users.delete_one({'username': dp.encrypt_text(username)})
if result.deleted_count == 0: if result.deleted_count == 0:
# Try with different field name result = users.delete_one({'Username': dp.encrypt_text(username)})
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = _get_tenant_db(client) client.close()
users = db['users']
result = users.delete_one({'Username': username})
client.close()
return result.deleted_count > 0 return result.deleted_count > 0
def update_active_borrowing(username, item_id, status): def update_active_borrowing(username, item_id, status):
""" """Update a user's active borrowing status."""
Update a user's active borrowing status.
Args:
username (str): Username of the user
item_id (str): ID of the borrowed item or None if returning
status (bool): True if borrowing, False if returning
Returns:
bool: True if successful, False on error
"""
try: try:
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = _get_tenant_db(client) db = _get_tenant_db(client)
users = db['users'] users = db['users']
result = users.update_one(
{'username': username}, update_data = {'$set': {'active_borrowing': status, 'borrowed_item': item_id if status else None}}
{'$set': {
'active_borrowing': status, result = users.update_one({'username': dp.encrypt_text(username)}, update_data)
'borrowed_item': item_id if status else None
}}
)
if result.matched_count == 0: if result.matched_count == 0:
result = users.update_one( result = users.update_one({'Username': dp.encrypt_text(username)}, update_data)
{'Username': username},
{'$set': {
'active_borrowing': status,
'borrowed_item': item_id if status else None
}}
)
client.close() client.close()
return result.modified_count > 0 return result.modified_count > 0
except Exception as e: except Exception as e:
@@ -840,43 +736,37 @@ def update_active_borrowing(username, item_id, status):
def get_name(username): def get_name(username):
""" """Retrieve the name that is associated with the username."""
Retrieve the name that is assosiated with the username.
Returns:
str: String of name
"""
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = _get_tenant_db(client) db = _get_tenant_db(client)
users = db['users'] users = db['users']
user = users.find_one({'Username': username})
name = user.get("name") user = users.find_one({'Username': dp.encrypt_text(username)}) or users.find_one({'username': dp.encrypt_text(username)})
return name client.close()
if not user or not user.get("name"):
return ""
return dp.decrypt_text(user.get("name"))
def get_last_name(username): def get_last_name(username):
""" """Retrieve the last_name that is associated with the username."""
Retrieve the last_name that is assosiated with the username.
Returns:
str: String of last_name
"""
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = _get_tenant_db(client) db = _get_tenant_db(client)
users = db['users'] users = db['users']
user = users.find_one({'Username': username})
name = user.get("last_name") user = users.find_one({'Username': dp.encrypt_text(username)}) or users.find_one({'username': dp.encrypt_text(username)})
return name client.close()
if not user or not user.get("last_name"):
return ""
return dp.decrypt_text(user.get("last_name"))
def get_all_users(): def get_all_users():
""" """Retrieve all users from the database."""
Retrieve all users from the database.
Administrative function for user management.
Returns:
list: List of all user documents
"""
try: try:
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = _get_tenant_db(client) db = _get_tenant_db(client)
@@ -887,65 +777,58 @@ def get_all_users():
except Exception as e: except Exception as e:
return [] return []
def update_password(username, new_password): def update_password(username, new_password):
""" """Update a user's password with a new one."""
Update a user's password with a new one.
Args:
username (str): Username of the user
new_password (str): New password to set
Returns:
bool: True if password was updated successfully, False otherwise
"""
try: try:
if not check_password_strength(new_password): if not check_password_strength(new_password):
return False return False
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = _get_tenant_db(client) db = _get_tenant_db(client)
users = db['users'] users = db['users']
# Hash the new password
hashed_password = hashing(new_password) hashed_password = hashing(new_password)
# Update the user's password
result = users.update_one( result = users.update_one(
{'Username': username}, {'Username': dp.encrypt_text(username)},
{'$set': {'Password': hashed_password}} {'$set': {'Password': hashed_password}}
) )
if result.matched_count == 0:
result = users.update_one(
{'username': dp.encrypt_text(username)},
{'$set': {'Password': hashed_password}}
)
client.close() client.close()
return result.modified_count > 0 return result.modified_count > 0
except Exception as e: except Exception as e:
print(f"Error updating password: {e}") print(f"Error updating password: {e}")
return False return False
def update_user_name(username, name, last_name): def update_user_name(username, name, last_name):
""" """Update a user's name and last name."""
Update a user's name and last name.
Args:
username (str): Username of the user
name (str): New first name
last_name (str): New last name
Returns:
bool: True if updated successfully, False otherwise
"""
try: try:
name_alias = build_name_synonym(name, last_name)
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
db = _get_tenant_db(client) db = _get_tenant_db(client)
users = db['users'] users = db['users']
safe_name = dp.encrypt_text(name.strip()) if name else ''
safe_last_name = dp.encrypt_text(last_name.strip()) if last_name else ''
result = users.update_one( result = users.update_one(
{'Username': username}, {'Username': dp.encrypt_text(username)},
{'$set': {'name': name_alias, 'last_name': ''}} {'$set': {'name': safe_name, 'last_name': safe_last_name}}
) )
if result.matched_count == 0:
result = users.update_one(
{'username': dp.encrypt_text(username)},
{'$set': {'name': safe_name, 'last_name': safe_last_name}}
)
client.close() client.close()
return True return True
except Exception as e: except Exception as e:
print(f"Error updating user name: {e}") print(f"Error updating user name: {e}")
return False return False
+2 -2
View File
@@ -37,14 +37,14 @@ def send(email: list | str, subject: str, note: str, sender: str) -> bool:
<tr> <tr>
<td> <td>
<p style="margin:0 0 12px 0;">Mit freundlichen Grüßen</p> <p style="margin:0 0 12px 0;">Mit freundlichen Grüßen</p>
<p style="margin:0;"><strong style="font-size:16px;">Automatisierter Email Verteiler für die Schule: {cfg.get_school_info().get("name")}</strong><br></p> <p style="margin:0;"><strong style="font-size:16px;">Automatisierter Email Verteiler für die Schule: {cfg.get_school_info().get("name")}</strong><br></p><br>
<p style="margin:12px 0 0 0;"><strong>Invario UG</strong><br>Am Sportplatz 10<br>83052 Bruckmühl</p> <p style="margin:12px 0 0 0;"><strong>Invario UG</strong><br>Am Sportplatz 10<br>83052 Bruckmühl</p>
</td> </td>
</tr> </tr>
</table> </table>
""" """
text_content = f"{body_message}\n\nMit freundlichen Grüßen\n{sender}\nInvario UG" text_content = f"{body_message}\n\nMit freundlichen Grüßen\n{sender}\n"
html_content = f""" html_content = f"""
<html> <html>
+2 -2
View File
@@ -252,8 +252,8 @@ def new(date_start: str, date_end: str, time_span: list, slots, slot_length, use
if calendar_link: if calendar_link:
email_body += f"\n\nKalendereintrag: {calendar_link}" email_body += f"\n\nKalendereintrag: {calendar_link}"
if normalized_mail and cfg.EMAIL_ENABLED: #if normalized_mail and cfg.EMAIL_ENABLED:
mail_service.send(normalized_mail, subject, email_body) mail_service.send(normalized_mail, subject, email_body, f"Terminplanungssystem {cfg.SCHOOL_INFO_DEFAULT.get("name")}")
return { return {
'appointment_id': id_str, 'appointment_id': id_str,
+14 -3
View File
@@ -7,7 +7,7 @@
<div style="display:flex; justify-content:space-between; align-items:flex-start; gap:12px; flex-wrap:wrap; margin-bottom:16px;"> <div style="display:flex; justify-content:space-between; align-items:flex-start; gap:12px; flex-wrap:wrap; margin-bottom:16px;">
<div> <div>
<h1 style="margin:0;">Defekte Items</h1> <h1 style="margin:0;">Defekte Items</h1>
<p style="margin:6px 0 0; color:#64748b;">Eigenes Verwaltungsfenster fuer gemeldete Defekte mit schneller Reparatur-Funktion.</p> <p style="margin:6px 0 0; color:#64748b;">Eigenes Verwaltungsfenster für gemeldete Defekte mit schneller Reparatur-Funktion.</p>
</div> </div>
<div style="display:flex; gap:8px; flex-wrap:wrap;"> <div style="display:flex; gap:8px; flex-wrap:wrap;">
<a class="btn btn-outline-secondary" href="{{ url_for('admin_borrowings') }}">Ausleihen</a> <a class="btn btn-outline-secondary" href="{{ url_for('admin_borrowings') }}">Ausleihen</a>
@@ -42,7 +42,13 @@
{% if item.isbn %}<div style="font-size:0.82rem; color:#64748b;">ISBN: {{ item.isbn }}</div>{% endif %} {% if item.isbn %}<div style="font-size:0.82rem; color:#64748b;">ISBN: {{ item.isbn }}</div>{% endif %}
</td> </td>
<td style="padding:11px; border-bottom:1px solid #eef2f7; vertical-align:top;"> <td style="padding:11px; border-bottom:1px solid #eef2f7; vertical-align:top;">
<div style="display:inline-block; padding:3px 9px; border-radius:999px; background:#fee2e2; color:#991b1b; font-size:0.75rem; font-weight:700;">Defekt</div>
<!-- HIER IST DIE ÄNDERUNG: Dynamischer Status-Badge -->
<div class="badge bg-{{ item.status_color }} rounded-pill" style="font-size:0.75rem; font-weight:700; padding:4px 10px;">
{{ item.status_text }}
</div>
<!-- ENDE DER ÄNDERUNG -->
<div style="font-size:0.84rem; color:#475569; margin-top:6px;">Meldungen: {{ item.damage_count }}</div> <div style="font-size:0.84rem; color:#475569; margin-top:6px;">Meldungen: {{ item.damage_count }}</div>
<div style="font-size:0.84rem; color:#475569;">Condition: {{ item.condition or '-' }}</div> <div style="font-size:0.84rem; color:#475569;">Condition: {{ item.condition or '-' }}</div>
<div style="font-size:0.84rem; color:#475569;">Verfuegbar: {{ 'Ja' if item.available else 'Nein' }}</div> <div style="font-size:0.84rem; color:#475569;">Verfuegbar: {{ 'Ja' if item.available else 'Nein' }}</div>
@@ -66,7 +72,12 @@
{% endif %} {% endif %}
</td> </td>
<td style="padding:11px; border-bottom:1px solid #eef2f7; vertical-align:top;"> <td style="padding:11px; border-bottom:1px solid #eef2f7; vertical-align:top;">
<!-- Button wird ausgeblendet, falls das Item bereits repariert ist -->
{% if item.condition not in ['repaired', 'fixed'] %}
<button class="btn btn-success btn-sm" onclick="repairDamage('{{ item.id }}', this)">Als repariert markieren</button> <button class="btn btn-success btn-sm" onclick="repairDamage('{{ item.id }}', this)">Als repariert markieren</button>
{% else %}
<span class="text-success" style="font-size: 0.85rem;"><i class="bi bi-check-circle"></i> Repariert</span>
{% endif %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
@@ -118,4 +129,4 @@ function repairDamage(itemId, button) {
}); });
} }
</script> </script>
{% endblock %} {% endblock %}
+5 -5
View File
@@ -1148,7 +1148,7 @@
{% if current_permissions.pages.get('tutorial_page', False) %} {% if current_permissions.pages.get('tutorial_page', False) %}
<li><a class="dropdown-item" href="{{ url_for('tutorial_page') }}">Tutorial</a></li> <li><a class="dropdown-item" href="{{ url_for('tutorial_page') }}">Tutorial</a></li>
{% endif %} {% endif %}
{% if current_permissions.pages.get('admin_school_settings', False) %} {% if current_permissions.actions.get('can_manage_settings', False) %}
<li><a class="dropdown-item" href="{{ url_for('admin_school_settings') }}">Schulstammdaten</a></li> <li><a class="dropdown-item" href="{{ url_for('admin_school_settings') }}">Schulstammdaten</a></li>
{% endif %} {% endif %}
{% if current_permissions.actions.get('can_view_logs', False) or current_permissions.pages.get('admin_audit_dashboard', False) %} {% if current_permissions.actions.get('can_view_logs', False) or current_permissions.pages.get('admin_audit_dashboard', False) %}
@@ -1257,7 +1257,7 @@
{% if current_permissions.pages.get('manage_locations', False) %} {% if current_permissions.pages.get('manage_locations', False) %}
<li><a class="dropdown-item" href="{{ url_for('manage_locations') }}">Orte verwalten</a></li> <li><a class="dropdown-item" href="{{ url_for('manage_locations') }}">Orte verwalten</a></li>
{% endif %} {% endif %}
{% if current_permissions.pages.get('admin_school_settings', False) %} {% if current_permissions.actions.get('can_manage_settings', False) %}
<li><a class="dropdown-item" href="{{ url_for('admin_school_settings') }}">Schulstammdaten</a></li> <li><a class="dropdown-item" href="{{ url_for('admin_school_settings') }}">Schulstammdaten</a></li>
{% endif %} {% endif %}
{% if current_permissions.pages.get('admin_borrowings', False) %} {% if current_permissions.pages.get('admin_borrowings', False) %}
@@ -1379,7 +1379,7 @@
<li><a class="dropdown-item" href="{{ url_for('student_cards_admin') }}">Bibliotheksausweis</a></li> <li><a class="dropdown-item" href="{{ url_for('student_cards_admin') }}">Bibliotheksausweis</a></li>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if current_permissions.pages.get('admin_school_settings', False) %} {% if current_permissions.actions.get('can_manage_settings', False) %}
<li><a class="dropdown-item" href="{{ url_for('admin_school_settings') }}">Schulstammdaten</a></li> <li><a class="dropdown-item" href="{{ url_for('admin_school_settings') }}">Schulstammdaten</a></li>
{% endif %} {% endif %}
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
@@ -1698,7 +1698,7 @@
<option value="Orte verwalten"></option> <option value="Orte verwalten"></option>
{% endif %} {% endif %}
{% if current_permissions.pages.get('admin_school_settings', False) %} {% if current_permissions.actions.get('can_manage_settings', False) %}
<option value="Schulstammdaten"></option> <option value="Schulstammdaten"></option>
{% endif %} {% endif %}
@@ -1823,7 +1823,7 @@
{ label: 'Orte verwalten', keywords: ['orte verwalten', 'orte', 'location'], url: {{ url_for('manage_locations')|tojson }} }, { label: 'Orte verwalten', keywords: ['orte verwalten', 'orte', 'location'], url: {{ url_for('manage_locations')|tojson }} },
{% endif %} {% endif %}
{% if current_permissions.pages.get('admin_school_settings', False) %} {% if current_permissions.actions.get('can_manage_settings', False) %}
{ label: 'Schulstammdaten', keywords: ['schule', 'settings', 'stammdaten', 'school settings'], url: {{ url_for('admin_school_settings')|tojson }} }, { label: 'Schulstammdaten', keywords: ['schule', 'settings', 'stammdaten', 'school settings'], url: {{ url_for('admin_school_settings')|tojson }} },
{% endif %} {% endif %}
File diff suppressed because it is too large Load Diff
+5 -4
View File
@@ -200,6 +200,7 @@ sys.path.insert(0, "/app")
sys.path.insert(0, "/app/Web") sys.path.insert(0, "/app/Web")
from Web.modules.database import settings from Web.modules.database import settings
from pymongo import MongoClient from pymongo import MongoClient
import Web.modules.inventarsystem.data_protection as dp
tenant_id = sys.argv[1].lower() tenant_id = sys.argv[1].lower()
mode = sys.argv[2] mode = sys.argv[2]
@@ -244,14 +245,14 @@ page_permissions = {
"manage_locations": True, "manage_locations": True,
} }
if db.users.count_documents({"Username": "admin"}) == 0: if db.users.count_documents({"Username": dp.encrypt_text("admin")}) == 0:
db.users.insert_one({ db.users.insert_one({
"Username": "admin", "Username": dp.encrypt_text("admin"),
"Password": hashed_pw_string, "Password": hashed_pw_string,
"Admin": True, "Admin": True,
"active_ausleihung": None, "active_ausleihung": None,
"name": "Admin", "name": dp.encrypt_text("Admin"),
"last_name": "User", "last_name": dp.encrypt_text("User"),
"IsStudent": False, "IsStudent": False,
"PermissionPreset": "full_access", "PermissionPreset": "full_access",
"ActionPermissions": action_permissions, "ActionPermissions": action_permissions,