changes to the encryption to the users and Items
This commit is contained in:
+70
-380
@@ -19,6 +19,7 @@ Collection Structure:
|
||||
- Status fields: Verfuegbar, User (if currently borrowed)
|
||||
"""
|
||||
from bson.objectid import ObjectId
|
||||
from bson.errors import InvalidId
|
||||
import datetime
|
||||
import Web.modules.database.settings as cfg
|
||||
from Web.modules.database.settings import MongoClient
|
||||
@@ -101,37 +102,12 @@ def add_item(name, ort, beschreibung, images=None, filter=None, filter2=None, fi
|
||||
isbn=None, item_type='general', library_category=None, is_library=False):
|
||||
"""
|
||||
Add a new item to the inventory.
|
||||
|
||||
Args:
|
||||
name (str): Name of the item
|
||||
ort (str): Location of the item
|
||||
beschreibung (str): Description of the item
|
||||
images (list, optional): List of image filenames for the item
|
||||
filter (str, optional): Primary filter/category for the item
|
||||
filter2 (str, optional): Secondary filter/category for the item
|
||||
filter3 (str, optional): Tertiary filter/category for the item
|
||||
ansch_jahr (int, optional): Year of acquisition
|
||||
ansch_kost (float, optional): Cost of acquisition
|
||||
code_4 (str, optional): 4-digit identification code
|
||||
reservierbar (bool, optional): Whether the item can be reserved in advance
|
||||
series_group_id (str, optional): Shared group id for same-type batch items
|
||||
series_count (int, optional): Total items in the created batch
|
||||
series_position (int, optional): Position inside the batch (1-based)
|
||||
is_grouped_sub_item (bool, optional): Whether this item is hidden as sub-item
|
||||
parent_item_id (str, optional): Parent item id if this is a sub-item
|
||||
isbn (str, optional): ISBN for books or media items
|
||||
item_type (str, optional): Type of the item (e.g., 'general', 'book', 'cd')
|
||||
library_category (str, optional): Library category for the item
|
||||
|
||||
Returns:
|
||||
ObjectId: ID of the new item or None if failed
|
||||
"""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
|
||||
# Set default values for optional parameters
|
||||
if images is None:
|
||||
images = []
|
||||
|
||||
@@ -161,10 +137,7 @@ def add_item(name, ort, beschreibung, images=None, filter=None, filter2=None, fi
|
||||
'LastUpdated': datetime.datetime.now()
|
||||
}
|
||||
result = items.insert_one(item)
|
||||
item_id = result.inserted_id
|
||||
|
||||
client.close()
|
||||
return item_id
|
||||
return result.inserted_id
|
||||
except Exception as e:
|
||||
print(f"Error adding item: {e}")
|
||||
return None
|
||||
@@ -173,15 +146,9 @@ def add_item(name, ort, beschreibung, images=None, filter=None, filter2=None, fi
|
||||
def remove_item(id):
|
||||
"""
|
||||
Soft-delete an item from the inventory.
|
||||
|
||||
Args:
|
||||
id (str): ID of the item to remove
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
result = items.update_one(
|
||||
@@ -193,7 +160,6 @@ def remove_item(id):
|
||||
'Verfuegbar': False,
|
||||
}}
|
||||
)
|
||||
client.close()
|
||||
return result.modified_count > 0
|
||||
except Exception as e:
|
||||
print(f"Error removing item: {e}")
|
||||
@@ -203,29 +169,19 @@ def remove_item(id):
|
||||
def get_group_item_ids(id):
|
||||
"""
|
||||
Resolve all item ids that belong to the same grouped series as the given item.
|
||||
|
||||
For non-grouped items, this returns only the provided id.
|
||||
|
||||
Args:
|
||||
id (str): ID of any item in the group
|
||||
|
||||
Returns:
|
||||
list[str]: All related item IDs (including the parent item)
|
||||
"""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
|
||||
base_item = items.find_one(_active_record_query({'_id': ObjectId(id)}))
|
||||
if not base_item:
|
||||
client.close()
|
||||
return []
|
||||
|
||||
resolved_ids = set()
|
||||
|
||||
# Prefer SeriesGroupId because it represents the full logical group.
|
||||
series_group_id = base_item.get('SeriesGroupId')
|
||||
|
||||
if series_group_id:
|
||||
for group_item in items.find(_active_record_query({'SeriesGroupId': series_group_id}), {'_id': 1}):
|
||||
resolved_ids.add(str(group_item['_id']))
|
||||
@@ -241,7 +197,6 @@ def get_group_item_ids(id):
|
||||
for child in items.find(_active_record_query({'ParentItemId': str(base_item['_id']), 'IsGroupedSubItem': True}), {'_id': 1}):
|
||||
resolved_ids.add(str(child['_id']))
|
||||
|
||||
client.close()
|
||||
return list(resolved_ids)
|
||||
except Exception as e:
|
||||
print(f"Error resolving group item IDs: {e}")
|
||||
@@ -251,7 +206,7 @@ def get_group_item_ids(id):
|
||||
def update_item(id, name, ort, beschreibung, images, verfuegbar, filter1, filter2, filter3,
|
||||
ansch_jahr, ansch_kost, code_4, reservierbar, isbn=None, item_type='general'):
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
|
||||
@@ -292,26 +247,18 @@ def update_item(id, name, ort, beschreibung, images, verfuegbar, filter1, filter
|
||||
{'$set': shared_update}
|
||||
)
|
||||
|
||||
client.close()
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error updating item: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def update_item_status(id, verfuegbar, user=None):
|
||||
"""
|
||||
Update the availability status of an inventory item.
|
||||
|
||||
Args:
|
||||
id (str): ID of the item to update
|
||||
verfuegbar (bool): New availability status
|
||||
user (str, optional): Username of person who borrowed the item
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
|
||||
@@ -325,7 +272,6 @@ def update_item_status(id, verfuegbar, user=None):
|
||||
if user is not None:
|
||||
update_data['User'] = dp.encrypt_text(user)
|
||||
elif verfuegbar:
|
||||
# If item is being marked as available, clear the user field
|
||||
update_query['$unset'] = {'User': ""}
|
||||
|
||||
result = items.update_one(
|
||||
@@ -333,7 +279,6 @@ def update_item_status(id, verfuegbar, user=None):
|
||||
update_query
|
||||
)
|
||||
|
||||
client.close()
|
||||
return result.modified_count > 0
|
||||
except Exception as e:
|
||||
print(f"Error updating item status: {e}")
|
||||
@@ -343,16 +288,9 @@ def update_item_status(id, verfuegbar, user=None):
|
||||
def update_item_exemplare_status(id, exemplare_status):
|
||||
"""
|
||||
Update the exemplar status of an inventory item.
|
||||
|
||||
Args:
|
||||
id (str): ID of the item to update
|
||||
exemplare_status (list): List of status objects for each exemplar
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
|
||||
@@ -366,7 +304,6 @@ def update_item_exemplare_status(id, exemplare_status):
|
||||
{'$set': update_data}
|
||||
)
|
||||
|
||||
client.close()
|
||||
return result.modified_count > 0
|
||||
except Exception as e:
|
||||
print(f"Error updating exemplar status: {e}")
|
||||
@@ -376,33 +313,20 @@ def update_item_exemplare_status(id, exemplare_status):
|
||||
def is_code_unique(code_4, exclude_id=None):
|
||||
"""
|
||||
Check if a given code is unique (not used by any other item).
|
||||
|
||||
Args:
|
||||
code_4 (str): The code to check
|
||||
exclude_id (str, optional): ID of item to exclude from the check (for edit operations)
|
||||
|
||||
Returns:
|
||||
bool: True if code is unique, False if already in use
|
||||
"""
|
||||
if not code_4 or code_4.strip() == "":
|
||||
# Empty codes are not considered unique
|
||||
return False
|
||||
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
|
||||
# Build query to find items with this code
|
||||
query = {'Code_4': code_4, 'Deleted': {'$ne': True}}
|
||||
|
||||
# If we're editing an item, exclude it from the uniqueness check
|
||||
if exclude_id:
|
||||
query['_id'] = {'$ne': ObjectId(exclude_id)}
|
||||
|
||||
# Check if any items with this code exist
|
||||
count = items.count_documents(query)
|
||||
|
||||
client.close()
|
||||
return count == 0
|
||||
|
||||
|
||||
@@ -411,20 +335,18 @@ def is_code_unique(code_4, exclude_id=None):
|
||||
def get_items():
|
||||
"""
|
||||
Retrieve all inventory items.
|
||||
|
||||
Returns:
|
||||
list: List of all inventory item documents with string IDs
|
||||
"""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
items_return = items.find(_active_record_query(_non_library_query()))
|
||||
|
||||
items_list = []
|
||||
for item in items_return:
|
||||
item['_id'] = str(item['_id'])
|
||||
items_list.append(item)
|
||||
client.close()
|
||||
|
||||
return items_list
|
||||
except Exception as e:
|
||||
print(f"Error retrieving items: {e}")
|
||||
@@ -434,20 +356,18 @@ def get_items():
|
||||
def get_available_items():
|
||||
"""
|
||||
Retrieve all available inventory items.
|
||||
|
||||
Returns:
|
||||
list: List of available inventory item documents with string IDs
|
||||
"""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
items_return = items.find(_active_record_query(_non_library_query({'Verfuegbar': True})))
|
||||
|
||||
items_list = []
|
||||
for item in items_return:
|
||||
item['_id'] = str(item['_id'])
|
||||
items_list.append(item)
|
||||
client.close()
|
||||
|
||||
return items_list
|
||||
except Exception as e:
|
||||
print(f"Error retrieving available items: {e}")
|
||||
@@ -457,25 +377,24 @@ def get_available_items():
|
||||
def get_borrowed_items():
|
||||
"""
|
||||
Retrieve all currently borrowed inventory items.
|
||||
|
||||
Returns:
|
||||
list: List of borrowed inventory item documents with string IDs
|
||||
"""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
items_return = items.find(_active_record_query(_non_library_query({'Verfuegbar': False})))
|
||||
|
||||
items_list = []
|
||||
for item in items_return:
|
||||
item['_id'] = str(item['_id'])
|
||||
items_list.append(item)
|
||||
client.close()
|
||||
|
||||
return items_list
|
||||
except Exception as e:
|
||||
print(f"Error retrieving borrowed items: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def get_item(id, decrypt=True):
|
||||
"""
|
||||
Retrieve an inventory item by ID, with optional decryption.
|
||||
@@ -485,11 +404,12 @@ def get_item(id, decrypt=True):
|
||||
return None
|
||||
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
query = _active_record_query({'_id': item_id})
|
||||
item = items.find_one(query)
|
||||
|
||||
if item:
|
||||
item['_id'] = str(item['_id'])
|
||||
if decrypt:
|
||||
@@ -499,22 +419,16 @@ def get_item(id, decrypt=True):
|
||||
print(f"Error retrieving item {id}: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def get_item_by_name(name):
|
||||
"""
|
||||
Retrieve a specific inventory item by its name.
|
||||
|
||||
Args:
|
||||
name (str): Name of the item to retrieve
|
||||
|
||||
Returns:
|
||||
dict: The inventory item document or None if not found
|
||||
"""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
item = items.find_one(_active_record_query({'Name': name}))
|
||||
client.close()
|
||||
item = items.find_one(_active_record_query(_non_library_query({'Name': name})))
|
||||
return item
|
||||
except Exception as e:
|
||||
print(f"Error retrieving item by name: {e}")
|
||||
@@ -524,19 +438,12 @@ def get_item_by_name(name):
|
||||
def get_items_by_filter(filter_value):
|
||||
"""
|
||||
Retrieve inventory items matching a specific filter/category.
|
||||
|
||||
Args:
|
||||
filter_value (str): Filter value to search for
|
||||
|
||||
Returns:
|
||||
list: List of items matching the filter in primary, secondary, or tertiary category
|
||||
"""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
|
||||
# Use $or to find matches in any filter field
|
||||
query = _active_record_query(_non_library_query({
|
||||
'$or': [
|
||||
{'Filter': filter_value},
|
||||
@@ -546,9 +453,6 @@ def get_items_by_filter(filter_value):
|
||||
}))
|
||||
|
||||
results = list(items.find(query))
|
||||
client.close()
|
||||
|
||||
# Convert ObjectId to string
|
||||
for item in results:
|
||||
item['_id'] = str(item['_id'])
|
||||
|
||||
@@ -561,29 +465,24 @@ def get_items_by_filter(filter_value):
|
||||
def get_filters():
|
||||
"""
|
||||
Retrieve all unique filter/category values from the inventory.
|
||||
|
||||
Returns:
|
||||
list: Combined list of all primary, secondary and tertiary filter values
|
||||
"""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
non_library = _active_record_query(_non_library_query())
|
||||
|
||||
filters = items.distinct('Filter', non_library)
|
||||
filters2 = items.distinct('Filter2', non_library)
|
||||
filters3 = items.distinct('Filter3', non_library)
|
||||
|
||||
# Combine filters and remove None/empty values
|
||||
all_filters = [f for f in filters + filters2 + filters3 if f]
|
||||
|
||||
# Remove duplicates while preserving order
|
||||
unique_filters = []
|
||||
for f in all_filters:
|
||||
if f not in unique_filters:
|
||||
unique_filters.append(f)
|
||||
|
||||
client.close()
|
||||
return unique_filters
|
||||
except Exception as e:
|
||||
print(f"Error retrieving filters: {e}")
|
||||
@@ -591,20 +490,13 @@ def get_filters():
|
||||
|
||||
|
||||
def get_primary_filters():
|
||||
"""
|
||||
Retrieve all unique primary filter values.
|
||||
|
||||
Returns:
|
||||
list: List of all primary filter values
|
||||
"""
|
||||
"""Retrieve all unique primary filter values."""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
filters = [f for f in items.distinct('Filter', _active_record_query(_non_library_query())) if f]
|
||||
client.close()
|
||||
|
||||
# Add predefined values
|
||||
predefined = get_predefined_filter_values(1)
|
||||
return sorted(list(set(filters + predefined)))
|
||||
except Exception as e:
|
||||
@@ -613,20 +505,13 @@ def get_primary_filters():
|
||||
|
||||
|
||||
def get_secondary_filters():
|
||||
"""
|
||||
Retrieve all unique secondary filter values.
|
||||
|
||||
Returns:
|
||||
list: List of all secondary filter values
|
||||
"""
|
||||
"""Retrieve all unique secondary filter values."""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
filters = [f for f in items.distinct('Filter2', _active_record_query(_non_library_query())) if f]
|
||||
client.close()
|
||||
|
||||
# Add predefined values
|
||||
predefined = get_predefined_filter_values(2)
|
||||
return sorted(list(set(filters + predefined)))
|
||||
except Exception as e:
|
||||
@@ -635,20 +520,13 @@ def get_secondary_filters():
|
||||
|
||||
|
||||
def get_tertiary_filters():
|
||||
"""
|
||||
Retrieve all unique tertiary filter values.
|
||||
|
||||
Returns:
|
||||
list: List of all tertiary filter values
|
||||
"""
|
||||
"""Retrieve all unique tertiary filter values."""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
filters = [f for f in items.distinct('Filter3', _active_record_query(_non_library_query())) if f]
|
||||
client.close()
|
||||
|
||||
# Add predefined values
|
||||
predefined = get_predefined_filter_values(3)
|
||||
return sorted(list(set(filters + predefined)))
|
||||
except Exception as e:
|
||||
@@ -659,24 +537,16 @@ def get_tertiary_filters():
|
||||
def get_item_by_code_4(code_4):
|
||||
"""
|
||||
Retrieve inventory items matching a specific 4-digit code.
|
||||
|
||||
Args:
|
||||
code_4 (str): 4-digit code to search for
|
||||
|
||||
Returns:
|
||||
list: List of items matching the code
|
||||
"""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
results = list(items.find(_active_record_query(_non_library_query({"Code_4": code_4}))))
|
||||
|
||||
# Convert ObjectId to string
|
||||
for item in results:
|
||||
item['_id'] = str(item['_id'])
|
||||
|
||||
client.close()
|
||||
return results
|
||||
except Exception as e:
|
||||
print(f"Error retrieving item by code: {e}")
|
||||
@@ -688,19 +558,13 @@ def get_item_by_code_4(code_4):
|
||||
def unstuck_item(id):
|
||||
"""
|
||||
Remove all borrowing records for a specific item to reset its status.
|
||||
Used to fix problematic or stuck items.
|
||||
|
||||
Args:
|
||||
id (str): ID of the item to unstick
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
ausleihungen = db['ausleihungen']
|
||||
result = ausleihungen.update_many(
|
||||
|
||||
ausleihungen.update_many(
|
||||
{'Item': id, 'Status': {'$nin': ['cancelled', 'deleted']}},
|
||||
{'$set': {
|
||||
'Status': 'cancelled',
|
||||
@@ -709,7 +573,6 @@ def unstuck_item(id):
|
||||
}}
|
||||
)
|
||||
|
||||
# Also reset the item status
|
||||
items = db['items']
|
||||
items.update_one(
|
||||
{'_id': ObjectId(id)},
|
||||
@@ -721,8 +584,6 @@ def unstuck_item(id):
|
||||
'$unset': {'User': ""}
|
||||
}
|
||||
)
|
||||
|
||||
client.close()
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error unsticking item: {e}")
|
||||
@@ -732,128 +593,79 @@ def unstuck_item(id):
|
||||
def get_predefined_filter_values(filter_num):
|
||||
"""
|
||||
Get predefined values for a specific filter.
|
||||
|
||||
Args:
|
||||
filter_num (int): Filter number (1 for Unterrichtsfach, 2 for Jahrgangsstufe)
|
||||
|
||||
Returns:
|
||||
list: List of predefined filter values
|
||||
"""
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
|
||||
# Use a dedicated collection for filter presets
|
||||
filter_presets = db['filter_presets']
|
||||
|
||||
# Find the document for the specified filter
|
||||
filter_doc = filter_presets.find_one({'filter_num': filter_num})
|
||||
|
||||
client.close()
|
||||
|
||||
if filter_doc and 'values' in filter_doc:
|
||||
# Sort values alphabetically
|
||||
return sorted(filter_doc['values'])
|
||||
else:
|
||||
# Create empty document if it doesn't exist
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
db = client[cfg.MONGODB_DB]
|
||||
filter_presets = db['filter_presets']
|
||||
filter_presets.update_one(
|
||||
{'filter_num': filter_num},
|
||||
{'$set': {'values': []}},
|
||||
upsert=True
|
||||
)
|
||||
client.close()
|
||||
return []
|
||||
|
||||
|
||||
def add_predefined_filter_value(filter_num, value):
|
||||
"""
|
||||
Add a new predefined value to a filter.
|
||||
|
||||
Args:
|
||||
filter_num (int): Filter number (1 for Unterrichtsfach, 2 for Jahrgangsstufe)
|
||||
value (str): Value to add
|
||||
|
||||
Returns:
|
||||
bool: True if value was added, False if it already existed
|
||||
"""
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
filter_presets = db['filter_presets']
|
||||
|
||||
# Check if value already exists
|
||||
filter_doc = filter_presets.find_one({
|
||||
'filter_num': filter_num,
|
||||
'values': value
|
||||
})
|
||||
|
||||
if filter_doc:
|
||||
# Value already exists
|
||||
client.close()
|
||||
return False
|
||||
|
||||
# Add the value to the filter
|
||||
result = filter_presets.update_one(
|
||||
{'filter_num': filter_num},
|
||||
{'$push': {'values': value}},
|
||||
upsert=True
|
||||
)
|
||||
|
||||
client.close()
|
||||
return result.modified_count > 0 or result.upserted_id is not None
|
||||
|
||||
|
||||
def remove_predefined_filter_value(filter_num, value):
|
||||
"""
|
||||
Remove a predefined value from a filter.
|
||||
|
||||
Args:
|
||||
filter_num (int): Filter number (1 for Unterrichtsfach, 2 for Jahrgangsstufe)
|
||||
value (str): Value to remove
|
||||
|
||||
Returns:
|
||||
bool: True if value was removed, False otherwise
|
||||
"""
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
filter_presets = db['filter_presets']
|
||||
|
||||
# Remove the value from the filter
|
||||
result = filter_presets.update_one(
|
||||
{'filter_num': filter_num},
|
||||
{'$pull': {'values': value}}
|
||||
)
|
||||
|
||||
client.close()
|
||||
return result.modified_count > 0
|
||||
|
||||
|
||||
def edit_predefined_filter_value(filter_num, old_value, new_value):
|
||||
"""
|
||||
Edit a predefined value from a filter and update all matching items.
|
||||
|
||||
Args:
|
||||
filter_num (int): Filter number (1 for Unterrichtsfach, 2 for Jahrgangsstufe)
|
||||
old_value (str): Value to replace
|
||||
new_value (str): New value
|
||||
|
||||
Returns:
|
||||
bool: True if value was updated, False otherwise
|
||||
"""
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
filter_presets = db['filter_presets']
|
||||
|
||||
# Check if the new value already exists
|
||||
existing = filter_presets.find_one({
|
||||
'filter_num': filter_num,
|
||||
'values': new_value
|
||||
})
|
||||
|
||||
if existing and old_value != new_value:
|
||||
client.close()
|
||||
return False
|
||||
|
||||
# Update the value in the filter
|
||||
result = filter_presets.update_one(
|
||||
{'filter_num': filter_num, 'values': old_value},
|
||||
{'$set': {'values.$': new_value}}
|
||||
@@ -863,41 +675,42 @@ def edit_predefined_filter_value(filter_num, old_value, new_value):
|
||||
items = db['items']
|
||||
filter_field = 'Filter' if filter_num == 1 else f'Filter{filter_num}'
|
||||
|
||||
# Also update all items that use this filter
|
||||
items.update_many(
|
||||
{filter_field: old_value},
|
||||
{'$set': {filter_field: new_value}}
|
||||
)
|
||||
|
||||
client.close()
|
||||
return result.modified_count > 0
|
||||
|
||||
|
||||
def get_filter_names():
|
||||
"""Get customized filter category names."""
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
names_doc = db.settings.find_one({'setting_type': 'filter_names'})
|
||||
client.close()
|
||||
|
||||
if names_doc and 'names' in names_doc:
|
||||
return names_doc['names']
|
||||
|
||||
return {
|
||||
'1': 'Fach/Kategorie',
|
||||
'2': 'System/Bereich',
|
||||
'3': 'Typ/Art'
|
||||
}
|
||||
|
||||
|
||||
def set_filter_name(filter_num, name):
|
||||
"""Set custom name for a filter category."""
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
names = get_filter_names()
|
||||
names[str(filter_num)] = name
|
||||
|
||||
db.settings.update_one(
|
||||
{'setting_type': 'filter_names'},
|
||||
{'$set': {'names': names}},
|
||||
upsert=True
|
||||
)
|
||||
client.close()
|
||||
return True
|
||||
|
||||
|
||||
@@ -906,33 +719,25 @@ def set_filter_name(filter_num, name):
|
||||
def get_predefined_locations():
|
||||
"""
|
||||
Get list of all predefined locations/placement options.
|
||||
|
||||
Returns:
|
||||
list: List of predefined location strings
|
||||
"""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
|
||||
# Check if settings collection exists, create if not
|
||||
if 'settings' not in db.list_collection_names():
|
||||
db.create_collection('settings')
|
||||
|
||||
# Get settings document or create if it doesn't exist
|
||||
settings_collection = db['settings']
|
||||
location_settings = settings_collection.find_one({'setting_type': 'predefined_locations'})
|
||||
|
||||
if not location_settings:
|
||||
# Create default settings document if it doesn't exist
|
||||
settings_collection.insert_one({
|
||||
'setting_type': 'predefined_locations',
|
||||
'locations': []
|
||||
})
|
||||
return []
|
||||
|
||||
# Return the predefined locations
|
||||
locations = location_settings.get('locations', [])
|
||||
client.close()
|
||||
return sorted(locations)
|
||||
|
||||
except Exception as e:
|
||||
@@ -943,12 +748,6 @@ def get_predefined_locations():
|
||||
def add_predefined_location(location):
|
||||
"""
|
||||
Add a new predefined location.
|
||||
|
||||
Args:
|
||||
location (str): Location to add
|
||||
|
||||
Returns:
|
||||
bool: True if added successfully, False if already exists
|
||||
"""
|
||||
if not location or not isinstance(location, str):
|
||||
return False
|
||||
@@ -958,35 +757,27 @@ def add_predefined_location(location):
|
||||
return False
|
||||
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
settings_collection = db['settings']
|
||||
|
||||
# Check if settings document exists, create if not
|
||||
location_settings = settings_collection.find_one({'setting_type': 'predefined_locations'})
|
||||
|
||||
if not location_settings:
|
||||
# Create with the new location
|
||||
settings_collection.insert_one({
|
||||
'setting_type': 'predefined_locations',
|
||||
'locations': [location]
|
||||
})
|
||||
client.close()
|
||||
return True
|
||||
|
||||
# Check if location already exists (case-insensitive)
|
||||
current_locations = location_settings.get('locations', [])
|
||||
if any(loc.lower() == location.lower() for loc in current_locations):
|
||||
client.close()
|
||||
return False
|
||||
|
||||
# Add the new location
|
||||
settings_collection.update_one(
|
||||
{'setting_type': 'predefined_locations'},
|
||||
{'$push': {'locations': location}}
|
||||
)
|
||||
|
||||
client.close()
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
@@ -997,18 +788,12 @@ def add_predefined_location(location):
|
||||
def remove_predefined_location(location):
|
||||
"""
|
||||
Remove a predefined location.
|
||||
|
||||
Args:
|
||||
location (str): Location to remove
|
||||
|
||||
Returns:
|
||||
bool: True if removed successfully
|
||||
"""
|
||||
if not location:
|
||||
return False
|
||||
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
settings_collection = db['settings']
|
||||
|
||||
@@ -1016,8 +801,6 @@ def remove_predefined_location(location):
|
||||
{'setting_type': 'predefined_locations'},
|
||||
{'$pull': {'locations': location}}
|
||||
)
|
||||
|
||||
client.close()
|
||||
return result.modified_count > 0
|
||||
|
||||
except Exception as e:
|
||||
@@ -1028,129 +811,36 @@ def remove_predefined_location(location):
|
||||
def update_item_next_appointment(item_id, appointment_data):
|
||||
"""
|
||||
Update an item with information about its next scheduled appointment.
|
||||
|
||||
Args:
|
||||
item_id (str): ID of the item to update
|
||||
appointment_data (dict): Appointment information containing:
|
||||
- date: Date of the appointment
|
||||
- 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:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
with MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT) as client:
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
|
||||
# Format the appointment data for storage
|
||||
# Ensure date is a datetime object for MongoDB storage
|
||||
appointment_date = appointment_data['date']
|
||||
if isinstance(appointment_date, datetime.date) and not isinstance(appointment_date, datetime.datetime):
|
||||
# Convert date to datetime for MongoDB compatibility
|
||||
appointment_date = datetime.datetime.combine(appointment_date, datetime.time())
|
||||
|
||||
next_appointment = {
|
||||
'date': appointment_date,
|
||||
'end_date': appointment_data.get('end_date', appointment_date),
|
||||
'start_period': appointment_data['start_period'],
|
||||
'end_period': appointment_data['end_period'],
|
||||
'user': dp.encrypt_text(appointment_data['user']),
|
||||
'notes': appointment_data.get('notes', ''),
|
||||
'appointment_id': appointment_data['appointment_id'],
|
||||
'scheduled_at': datetime.datetime.now()
|
||||
if appointment_data is None:
|
||||
update_query = {
|
||||
'$unset': {'NextAppointment': ""},
|
||||
'$set': {'LastUpdated': datetime.datetime.now()}
|
||||
}
|
||||
else:
|
||||
data_to_save = appointment_data.copy()
|
||||
|
||||
update_data = {
|
||||
'NextAppointment': next_appointment,
|
||||
if 'user' in data_to_save and data_to_save['user']:
|
||||
data_to_save['user'] = dp.encrypt_text(data_to_save['user'])
|
||||
|
||||
update_query = {
|
||||
'$set': {
|
||||
'NextAppointment': data_to_save,
|
||||
'LastUpdated': datetime.datetime.now()
|
||||
}
|
||||
}
|
||||
|
||||
result = items.update_one(
|
||||
{'_id': ObjectId(item_id)},
|
||||
{'$set': update_data}
|
||||
update_query
|
||||
)
|
||||
|
||||
client.close()
|
||||
return result.modified_count > 0
|
||||
except Exception as e:
|
||||
print(f"Error updating item next appointment: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def clear_item_next_appointment(item_id):
|
||||
"""
|
||||
Clear the next appointment information from an item.
|
||||
|
||||
Args:
|
||||
item_id (str): ID of the item to update
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
|
||||
result = items.update_one(
|
||||
{'_id': ObjectId(item_id)},
|
||||
{'$unset': {'NextAppointment': ""}, '$set': {'LastUpdated': datetime.datetime.now()}}
|
||||
)
|
||||
|
||||
client.close()
|
||||
return result.modified_count > 0
|
||||
except Exception as e:
|
||||
print(f"Error clearing item next appointment: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def get_items_with_appointments():
|
||||
"""
|
||||
Retrieve all items that have scheduled appointments.
|
||||
|
||||
Returns:
|
||||
list: List of items with NextAppointment field
|
||||
"""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
|
||||
items_return = items.find({'NextAppointment': {'$exists': True}, 'Deleted': {'$ne': True}})
|
||||
items_list = []
|
||||
for item in items_return:
|
||||
item['_id'] = str(item['_id'])
|
||||
items_list.append(item)
|
||||
client.close()
|
||||
return items_list
|
||||
except Exception as e:
|
||||
print(f"Error retrieving items with appointments: {e}")
|
||||
return []
|
||||
|
||||
def get_current_status(item_id, decrypt=True):
|
||||
"""
|
||||
Retrieve the current status of an item, decrypting the user field if present.
|
||||
"""
|
||||
oid = _to_object_id(item_id)
|
||||
if not oid:
|
||||
return None
|
||||
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
db = client[cfg.MONGODB_DB]
|
||||
items = db['items']
|
||||
item = items.find_one({'_id': oid}, {'Verfuegbar': 1, 'User': 1})
|
||||
if item:
|
||||
item['_id'] = str(item['_id'])
|
||||
if decrypt:
|
||||
decrypt_item_user_data(item)
|
||||
return item
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"Error retrieving current status for item {item_id}: {e}")
|
||||
return None
|
||||
+108
-231
@@ -110,13 +110,6 @@ def build_username_from_name(first_name, last_name=''):
|
||||
"""
|
||||
Build a deterministic username abbreviation from first and last name.
|
||||
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)
|
||||
return alias.lower()
|
||||
@@ -325,7 +318,6 @@ def get_effective_permissions(username):
|
||||
return build_default_permission_payload('full_access')
|
||||
|
||||
preset_key = user.get('PermissionPreset')
|
||||
print(preset_key)
|
||||
payload = build_default_permission_payload(preset_key)
|
||||
payload['actions'] = _normalize_bool_map(user.get('ActionPermissions', {}), payload['actions'])
|
||||
payload['pages'] = _normalize_bool_map(user.get('PagePermissions', {}), payload['pages'])
|
||||
@@ -353,10 +345,10 @@ def update_user_permissions(username, preset_key, action_permissions=None, page_
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
db = _get_tenant_db(client)
|
||||
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:
|
||||
result = users.update_one({'username': username}, {'$set': update_data})
|
||||
result = users.update_one({'username': dp.encrypt_text(username)}, {'$set': update_data})
|
||||
|
||||
client.close()
|
||||
return result.matched_count > 0
|
||||
@@ -368,7 +360,7 @@ def get_favorites(username):
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
db = _get_tenant_db(client)
|
||||
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()
|
||||
if not user:
|
||||
return []
|
||||
@@ -383,7 +375,7 @@ def add_favorite(username, item_id):
|
||||
db = _get_tenant_db(client)
|
||||
users = db['users']
|
||||
users.update_one(
|
||||
{'$or': [{'Username': username}, {'username': username}]},
|
||||
{'$or': [{'Username': dp.encrypt_text(username)}, {'username': dp.encrypt_text(username)}]},
|
||||
{'$addToSet': {'favorites': ObjectId(item_id)}}
|
||||
)
|
||||
client.close()
|
||||
@@ -398,7 +390,7 @@ def remove_favorite(username, item_id):
|
||||
db = _get_tenant_db(client)
|
||||
users = db['users']
|
||||
users.update_one(
|
||||
{'$or': [{'Username': username}, {'username': username}]},
|
||||
{'$or': [{'Username': dp.encrypt_text(username)}, {'username': dp.encrypt_text(username)}]},
|
||||
{'$pull': {'favorites': ObjectId(item_id)}}
|
||||
)
|
||||
client.close()
|
||||
@@ -407,16 +399,9 @@ def remove_favorite(username, item_id):
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def check_password_strength(password):
|
||||
"""
|
||||
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:
|
||||
return False
|
||||
@@ -437,18 +422,14 @@ def check_password_strength(password):
|
||||
def hashing(password, salt=None):
|
||||
"""
|
||||
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:
|
||||
# Neuer Benutzer / Passwortänderung -> Dynamischer Salt
|
||||
random_salt = os.urandom(16)
|
||||
hashed = hashlib.scrypt(password_bytes, salt=random_salt, n=16384, r=8, p=1)
|
||||
return f"v1${random_salt.hex()}${hashed.hex()}"
|
||||
else:
|
||||
# Bestehender Benutzer (wird zur Verifizierung aufgerufen)
|
||||
hashed = hashlib.scrypt(password_bytes, salt=salt, n=16384, r=8, p=1)
|
||||
return hashed.hex()
|
||||
|
||||
@@ -456,25 +437,20 @@ def hashing(password, salt=None):
|
||||
def verify_password(provided_password, stored_password_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:
|
||||
return False
|
||||
|
||||
# Überprüfung für das neue, sichere Format
|
||||
if stored_password_string.startswith("v1$"):
|
||||
try:
|
||||
_, salt_hex, hash_hex = stored_password_string.split("$")
|
||||
salt_bytes = bytes.fromhex(salt_hex)
|
||||
# Berechne den Hash des eingegebenen Passworts mit dem extrahierten Salt
|
||||
calculated_hash = hashing(provided_password, salt=salt_bytes)
|
||||
# Timing-Attack-sicherer Vergleich
|
||||
return hmac.compare_digest(calculated_hash, hash_hex)
|
||||
except (ValueError, TypeError):
|
||||
logger.error("Ungültiges Hash-Format in der Datenbank entdeckt.")
|
||||
return False
|
||||
else:
|
||||
# Abwärtskompatibilität: Altes Format mit statischem Salt b'some_salt'
|
||||
old_static_salt = b'some_salt'
|
||||
calculated_hash = hashing(provided_password, salt=old_static_salt)
|
||||
return hmac.compare_digest(calculated_hash, stored_password_string)
|
||||
@@ -496,21 +472,20 @@ def check_nm_pwd(username, password):
|
||||
db = client[db_name]
|
||||
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)
|
||||
|
||||
if user_record is None:
|
||||
logger.warning("Kein Benutzer für %r in DB %r gefunden.", username, db_name)
|
||||
logger.warning("Kein Benutzer für %r in DB %r gefunden.", dp.encrypt_text(username), db_name)
|
||||
return None
|
||||
|
||||
stored_password = user_record.get('Password') or user_record.get('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
|
||||
|
||||
# Automatische Migration alter Hashes auf das neue Format
|
||||
if not stored_password.startswith("v1$"):
|
||||
if stored_password and not stored_password.startswith("v1$"):
|
||||
users.update_one({'_id': user_record['_id']}, {'$set': {'Password': hashing(password)}})
|
||||
|
||||
return user_record
|
||||
@@ -532,40 +507,35 @@ def add_user(
|
||||
):
|
||||
"""
|
||||
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)
|
||||
try:
|
||||
db = _get_tenant_db(client)
|
||||
users = db['users']
|
||||
if not check_password_strength(password):
|
||||
return False
|
||||
|
||||
permission_defaults = build_default_permission_payload(permission_preset)
|
||||
|
||||
if isinstance(action_permissions, dict):
|
||||
for key, value in action_permissions.items():
|
||||
permission_defaults['actions'][str(key)] = bool(value)
|
||||
|
||||
if isinstance(page_permissions, dict):
|
||||
for key, value in page_permissions.items():
|
||||
permission_defaults['pages'][str(key)] = bool(value)
|
||||
|
||||
if permission_preset == "full_access":
|
||||
can_admin_preset_based = True
|
||||
else:
|
||||
can_admin_preset_based = False
|
||||
safe_name = name.strip() if name else ''
|
||||
safe_last_name = last_name.strip() if last_name else ''
|
||||
|
||||
user_doc = {
|
||||
'Username': username,
|
||||
'Username': dp.encrypt_text(username),
|
||||
'Password': hashing(password),
|
||||
'Admin': can_admin_preset_based,
|
||||
'Admin': (permission_preset == "full_access"),
|
||||
'active_ausleihung': None,
|
||||
'name': name.strip() if name else '',
|
||||
'last_name': last_name.strip() if last_name else '',
|
||||
'name': dp.encrypt_text(safe_name) if safe_name else '',
|
||||
'last_name': dp.encrypt_text(safe_last_name) if safe_last_name else '',
|
||||
'IsStudent': bool(is_student),
|
||||
'PermissionPreset': permission_defaults['preset'],
|
||||
'ActionPermissions': permission_defaults['actions'],
|
||||
@@ -602,7 +572,7 @@ def student_card_exists(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)
|
||||
if not normalized:
|
||||
return None
|
||||
@@ -611,65 +581,44 @@ def get_user_by_student_card(student_card_id):
|
||||
users = db['student_cards']
|
||||
found_user = users.find_one({'SchülerName': normalized})
|
||||
client.close()
|
||||
|
||||
# Do not call dp.decrypt_text() here because found_user is a MongoDB dictionary.
|
||||
return found_user
|
||||
|
||||
|
||||
def make_admin(username):
|
||||
"""
|
||||
Grant administrator privileges to a user.
|
||||
|
||||
Args:
|
||||
username (str): Username of the user to promote
|
||||
|
||||
Returns:
|
||||
bool: True if user was promoted successfully
|
||||
"""
|
||||
"""Grant administrator privileges to a user."""
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
db = _get_tenant_db(client)
|
||||
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:
|
||||
result = users.update_one({'username': username}, {'$set': {'Admin': True}})
|
||||
result = users.update_one({'username': dp.encrypt_text(username)}, {'$set': {'Admin': True}})
|
||||
client.close()
|
||||
return result.matched_count > 0
|
||||
|
||||
|
||||
def remove_admin(username):
|
||||
"""
|
||||
Remove administrator privileges from a user.
|
||||
|
||||
Args:
|
||||
username (str): Username of the user to demote
|
||||
|
||||
Returns:
|
||||
bool: True if user was demoted successfully
|
||||
"""
|
||||
"""Remove administrator privileges from a user."""
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
db = _get_tenant_db(client)
|
||||
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:
|
||||
result = users.update_one({'username': username}, {'$set': {'Admin': False}})
|
||||
result = users.update_one({'username': dp.encrypt_text(username)}, {'$set': {'Admin': False}})
|
||||
client.close()
|
||||
return result.matched_count > 0
|
||||
|
||||
|
||||
def get_user(username):
|
||||
"""
|
||||
Retrieve a specific user by username.
|
||||
|
||||
Args:
|
||||
username (str): Username to search for
|
||||
|
||||
Returns:
|
||||
dict: User document or None if not found
|
||||
"""
|
||||
"""Retrieve a specific user by username."""
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
try:
|
||||
def find_in_db(database_name):
|
||||
db = client[database_name]
|
||||
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)})
|
||||
|
||||
# Try current tenant first when available
|
||||
tenant_db, tenant_id = _resolve_request_tenant_db()
|
||||
if tenant_db:
|
||||
user = find_in_db(tenant_db)
|
||||
@@ -682,7 +631,6 @@ def get_user(username):
|
||||
)
|
||||
return None
|
||||
|
||||
# Fallback to default configured database
|
||||
user = find_in_db(cfg.MONGODB_DB)
|
||||
if user:
|
||||
return user
|
||||
@@ -693,146 +641,88 @@ def get_user(username):
|
||||
|
||||
|
||||
def check_admin(username):
|
||||
"""
|
||||
Check if a user has administrator privileges.
|
||||
|
||||
Args:
|
||||
username (str): Username to check
|
||||
|
||||
Returns:
|
||||
bool: True if user is an administrator, False otherwise
|
||||
"""
|
||||
"""Check if a user has administrator privileges."""
|
||||
user = get_user(username)
|
||||
return bool(user and user.get('Admin', False))
|
||||
|
||||
|
||||
def update_active_ausleihung(username, id_item, ausleihung):
|
||||
"""
|
||||
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
|
||||
"""
|
||||
"""Update a user's active borrowing record."""
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
db = _get_tenant_db(client)
|
||||
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()
|
||||
return True
|
||||
|
||||
|
||||
def get_active_ausleihung(username):
|
||||
"""
|
||||
Get a user's active borrowing record.
|
||||
|
||||
Args:
|
||||
username (str): Username of the user
|
||||
|
||||
Returns:
|
||||
dict: Active borrowing information or None
|
||||
"""
|
||||
"""Get a user's active borrowing record."""
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
db = _get_tenant_db(client)
|
||||
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):
|
||||
"""
|
||||
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
|
||||
"""
|
||||
"""Check if a user currently has an active borrowing."""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
db = _get_tenant_db(client)
|
||||
users = db['users']
|
||||
|
||||
user = users.find_one({'username': username})
|
||||
if not user:
|
||||
user = 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()
|
||||
|
||||
if not user:
|
||||
client.close()
|
||||
return False
|
||||
|
||||
has_active = user.get('active_borrowing', False)
|
||||
|
||||
client.close()
|
||||
return has_active
|
||||
return user.get('active_borrowing', False)
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
|
||||
def delete_user(username):
|
||||
"""
|
||||
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
|
||||
"""
|
||||
"""Delete a user from the database."""
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
db = _get_tenant_db(client)
|
||||
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:
|
||||
# Try with different field name
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
db = _get_tenant_db(client)
|
||||
users = db['users']
|
||||
result = users.delete_one({'Username': username})
|
||||
client.close()
|
||||
result = users.delete_one({'Username': dp.encrypt_text(username)})
|
||||
|
||||
client.close()
|
||||
return result.deleted_count > 0
|
||||
|
||||
|
||||
def update_active_borrowing(username, item_id, 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
|
||||
"""
|
||||
"""Update a user's active borrowing status."""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
db = _get_tenant_db(client)
|
||||
users = db['users']
|
||||
result = users.update_one(
|
||||
{'username': username},
|
||||
{'$set': {
|
||||
'active_borrowing': status,
|
||||
'borrowed_item': item_id if status else None
|
||||
}}
|
||||
)
|
||||
|
||||
update_data = {'$set': {'active_borrowing': status, 'borrowed_item': item_id if status else None}}
|
||||
|
||||
result = users.update_one({'username': dp.encrypt_text(username)}, update_data)
|
||||
if result.matched_count == 0:
|
||||
result = users.update_one(
|
||||
{'Username': username},
|
||||
{'$set': {
|
||||
'active_borrowing': status,
|
||||
'borrowed_item': item_id if status else None
|
||||
}}
|
||||
)
|
||||
result = users.update_one({'Username': dp.encrypt_text(username)}, update_data)
|
||||
|
||||
client.close()
|
||||
return result.modified_count > 0
|
||||
@@ -841,43 +731,37 @@ def update_active_borrowing(username, item_id, status):
|
||||
|
||||
|
||||
def get_name(username):
|
||||
"""
|
||||
Retrieve the name that is assosiated with the username.
|
||||
|
||||
Returns:
|
||||
str: String of name
|
||||
"""
|
||||
"""Retrieve the name that is associated with the username."""
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
db = _get_tenant_db(client)
|
||||
users = db['users']
|
||||
user = users.find_one({'Username': username})
|
||||
name = user.get("name")
|
||||
return name
|
||||
|
||||
user = users.find_one({'Username': dp.encrypt_text(username)}) or users.find_one({'username': dp.encrypt_text(username)})
|
||||
client.close()
|
||||
|
||||
if not user or not user.get("name"):
|
||||
return ""
|
||||
|
||||
return dp.decrypt_text(user.get("name"))
|
||||
|
||||
|
||||
def get_last_name(username):
|
||||
"""
|
||||
Retrieve the last_name that is assosiated with the username.
|
||||
|
||||
Returns:
|
||||
str: String of last_name
|
||||
"""
|
||||
"""Retrieve the last_name that is associated with the username."""
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
db = _get_tenant_db(client)
|
||||
users = db['users']
|
||||
user = users.find_one({'Username': username})
|
||||
name = user.get("last_name")
|
||||
return name
|
||||
|
||||
user = users.find_one({'Username': dp.encrypt_text(username)}) or users.find_one({'username': dp.encrypt_text(username)})
|
||||
client.close()
|
||||
|
||||
if not user or not user.get("last_name"):
|
||||
return ""
|
||||
|
||||
return dp.decrypt_text(user.get("last_name"))
|
||||
|
||||
|
||||
def get_all_users():
|
||||
"""
|
||||
Retrieve all users from the database.
|
||||
Administrative function for user management.
|
||||
|
||||
Returns:
|
||||
list: List of all user documents
|
||||
"""
|
||||
"""Retrieve all users from the database."""
|
||||
try:
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
db = _get_tenant_db(client)
|
||||
@@ -888,17 +772,9 @@ def get_all_users():
|
||||
except Exception as e:
|
||||
return []
|
||||
|
||||
|
||||
def update_password(username, new_password):
|
||||
"""
|
||||
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
|
||||
"""
|
||||
"""Update a user's password with a new one."""
|
||||
try:
|
||||
if not check_password_strength(new_password):
|
||||
return False
|
||||
@@ -907,12 +783,15 @@ def update_password(username, new_password):
|
||||
db = _get_tenant_db(client)
|
||||
users = db['users']
|
||||
|
||||
# Hash the new password
|
||||
hashed_password = hashing(new_password)
|
||||
|
||||
# Update the user's password
|
||||
result = users.update_one(
|
||||
{'Username': username},
|
||||
{'Username': dp.encrypt_text(username)},
|
||||
{'$set': {'Password': hashed_password}}
|
||||
)
|
||||
if result.matched_count == 0:
|
||||
result = users.update_one(
|
||||
{'username': dp.encrypt_text(username)},
|
||||
{'$set': {'Password': hashed_password}}
|
||||
)
|
||||
|
||||
@@ -922,27 +801,25 @@ def update_password(username, new_password):
|
||||
print(f"Error updating password: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def update_user_name(username, name, 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
|
||||
"""
|
||||
"""Update a user's name and last name."""
|
||||
try:
|
||||
name_alias = build_name_synonym(name, last_name)
|
||||
client = MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
db = _get_tenant_db(client)
|
||||
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(
|
||||
{'Username': username},
|
||||
{'$set': {'name': name_alias, 'last_name': ''}}
|
||||
{'Username': dp.encrypt_text(username)},
|
||||
{'$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()
|
||||
|
||||
Reference in New Issue
Block a user