Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
M
MBetterc
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Mbetter
MBetterc
Commits
262296b4
Commit
262296b4
authored
Dec 20, 2025
by
Stefy Lanza (nextime / spora )
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update vets
parent
4890af51
Changes
6
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
736 additions
and
454 deletions
+736
-454
API_AUTHENTICATION_DOCUMENTATION.md
API_AUTHENTICATION_DOCUMENTATION.md
+207
-396
barcode_utils.py
mbetterclient/utils/barcode_utils.py
+152
-32
routes.py
mbetterclient/web_dashboard/routes.py
+91
-23
base.html
mbetterclient/web_dashboard/templates/base.html
+6
-3
test_auth_verification.py
test_auth_verification.py
+196
-0
test_config_auth.py
test_config_auth.py
+84
-0
No files found.
API_AUTHENTICATION_DOCUMENTATION.md
View file @
262296b4
This diff is collapsed.
Click to expand it.
mbetterclient/utils/barcode_utils.py
View file @
262296b4
...
@@ -58,20 +58,20 @@ def validate_barcode_data(data: str, standard: str) -> bool:
...
@@ -58,20 +58,20 @@ def validate_barcode_data(data: str, standard: str) -> bool:
return
len
(
data
)
<=
43
and
all
(
c
in
valid_chars
for
c
in
data
.
upper
())
return
len
(
data
)
<=
43
and
all
(
c
in
valid_chars
for
c
in
data
.
upper
())
elif
standard
==
'ean13'
:
elif
standard
==
'ean13'
:
# EAN-13 requires exactly 1
2 digits (13th is
check digit)
# EAN-13 requires exactly 1
3 digits (12 data + 1
check digit)
return
data
.
isdigit
()
and
len
(
data
)
==
1
2
return
data
.
isdigit
()
and
len
(
data
)
==
1
3
elif
standard
==
'ean8'
:
elif
standard
==
'ean8'
:
# EAN-8 requires exactly
7 digits (8th is
check digit)
# EAN-8 requires exactly
8 digits (7 data + 1
check digit)
return
data
.
isdigit
()
and
len
(
data
)
==
7
return
data
.
isdigit
()
and
len
(
data
)
==
8
elif
standard
==
'upca'
:
elif
standard
==
'upca'
:
# UPC-A requires exactly 1
1 digits (12th is
check digit)
# UPC-A requires exactly 1
2 digits (11 data + 1
check digit)
return
data
.
isdigit
()
and
len
(
data
)
==
1
1
return
data
.
isdigit
()
and
len
(
data
)
==
1
2
elif
standard
==
'upce'
:
elif
standard
==
'upce'
:
# UPC-E requires exactly
7 digits (8th is
check digit)
# UPC-E requires exactly
8 digits (7 data + 1
check digit)
return
data
.
isdigit
()
and
len
(
data
)
==
7
return
data
.
isdigit
()
and
len
(
data
)
==
8
elif
standard
==
'codabar'
:
elif
standard
==
'codabar'
:
# Codabar accepts digits and some special characters
# Codabar accepts digits and some special characters
...
@@ -106,6 +106,11 @@ def generate_barcode_image(data: str, standard: str, width: int = 300, height: i
...
@@ -106,6 +106,11 @@ def generate_barcode_image(data: str, standard: str, width: int = 300, height: i
return
None
return
None
try
:
try
:
# Special case for UPC-E which is not supported by python-barcode library
if
standard
==
'upce'
:
logger
.
warning
(
f
"UPC-E barcode generation not supported by python-barcode library. Data: {data}"
)
return
None
# Validate data first
# Validate data first
if
not
validate_barcode_data
(
data
,
standard
):
if
not
validate_barcode_data
(
data
,
standard
):
logger
.
error
(
f
"Invalid data '{data}' for barcode standard '{standard}'"
)
logger
.
error
(
f
"Invalid data '{data}' for barcode standard '{standard}'"
)
...
@@ -126,7 +131,6 @@ def generate_barcode_image(data: str, standard: str, width: int = 300, height: i
...
@@ -126,7 +131,6 @@ def generate_barcode_image(data: str, standard: str, width: int = 300, height: i
'ean13'
:
'ean13'
,
'ean13'
:
'ean13'
,
'ean8'
:
'ean8'
,
'ean8'
:
'ean8'
,
'upca'
:
'upc'
,
'upca'
:
'upc'
,
'upce'
:
'upce'
,
'codabar'
:
'codabar'
,
'codabar'
:
'codabar'
,
'itf'
:
'itf'
'itf'
:
'itf'
}
}
...
@@ -198,6 +202,118 @@ def generate_barcode_base64(data: str, standard: str, width: int = 300, height:
...
@@ -198,6 +202,118 @@ def generate_barcode_base64(data: str, standard: str, width: int = 300, height:
logger
.
error
(
f
"Failed to generate base64 barcode: {e}"
)
logger
.
error
(
f
"Failed to generate base64 barcode: {e}"
)
return
None
return
None
def
calculate_ean13_check_digit
(
data
:
str
)
->
str
:
"""
Calculate EAN-13 check digit for the given 12-digit data
Args:
data: 12-digit string
Returns:
Complete 13-digit EAN-13 code including check digit
"""
if
not
(
data
.
isdigit
()
and
len
(
data
)
==
12
):
raise
ValueError
(
"EAN-13 check digit calculation requires exactly 12 digits"
)
# EAN-13 check digit calculation
# Step 1: Sum digits in odd positions (1st, 3rd, 5th, etc.) multiplied by 3
odd_sum
=
sum
(
int
(
data
[
i
])
for
i
in
range
(
0
,
12
,
2
))
*
3
# Step 2: Sum digits in even positions (2nd, 4th, 6th, etc.)
even_sum
=
sum
(
int
(
data
[
i
])
for
i
in
range
(
1
,
12
,
2
))
# Step 3: Total sum
total
=
odd_sum
+
even_sum
# Step 4: Find the smallest number that makes total divisible by 10
check_digit
=
(
10
-
(
total
%
10
))
%
10
return
data
+
str
(
check_digit
)
def
calculate_ean8_check_digit
(
data
:
str
)
->
str
:
"""
Calculate EAN-8 check digit for the given 7-digit data
Args:
data: 7-digit string
Returns:
Complete 8-digit EAN-8 code including check digit
"""
if
not
(
data
.
isdigit
()
and
len
(
data
)
==
7
):
raise
ValueError
(
"EAN-8 check digit calculation requires exactly 7 digits"
)
# EAN-8 uses the same algorithm as EAN-13
# Step 1: Sum digits in odd positions (1st, 3rd, 5th, 7th) multiplied by 3
odd_sum
=
sum
(
int
(
data
[
i
])
for
i
in
range
(
0
,
7
,
2
))
*
3
# Step 2: Sum digits in even positions (2nd, 4th, 6th)
even_sum
=
sum
(
int
(
data
[
i
])
for
i
in
range
(
1
,
7
,
2
))
# Step 3: Total sum
total
=
odd_sum
+
even_sum
# Step 4: Find the smallest number that makes total divisible by 10
check_digit
=
(
10
-
(
total
%
10
))
%
10
return
data
+
str
(
check_digit
)
def
calculate_upca_check_digit
(
data
:
str
)
->
str
:
"""
Calculate UPC-A check digit for the given 11-digit data
Args:
data: 11-digit string
Returns:
Complete 12-digit UPC-A code including check digit
"""
if
not
(
data
.
isdigit
()
and
len
(
data
)
==
11
):
raise
ValueError
(
"UPC-A check digit calculation requires exactly 11 digits"
)
# UPC-A check digit calculation (different from EAN-13)
# Step 1: Sum digits in odd positions (1st, 3rd, 5th, 7th, 9th, 11th) multiplied by 3
odd_sum
=
sum
(
int
(
data
[
i
])
for
i
in
range
(
0
,
11
,
2
))
*
3
# Step 2: Sum digits in even positions (2nd, 4th, 6th, 8th, 10th)
even_sum
=
sum
(
int
(
data
[
i
])
for
i
in
range
(
1
,
11
,
2
))
# Step 3: Total sum
total
=
odd_sum
+
even_sum
# Step 4: Find the smallest number that makes total divisible by 10
check_digit
=
(
10
-
(
total
%
10
))
%
10
return
data
+
str
(
check_digit
)
def
calculate_upce_check_digit
(
data
:
str
)
->
str
:
"""
Calculate UPC-E check digit for the given 7-digit data
Args:
data: 7-digit string
Returns:
Complete 8-digit UPC-E code including check digit
"""
if
not
(
data
.
isdigit
()
and
len
(
data
)
==
7
):
raise
ValueError
(
"UPC-E check digit calculation requires exactly 7 digits"
)
# UPC-E check digit calculation
# First expand to UPC-A format, then calculate UPC-A check digit
# This is a simplified approach - UPC-E has complex expansion rules
# For our purposes, we'll use the same algorithm as UPC-A
odd_sum
=
sum
(
int
(
data
[
i
])
for
i
in
range
(
0
,
7
,
2
))
*
3
even_sum
=
sum
(
int
(
data
[
i
])
for
i
in
range
(
1
,
7
,
2
))
total
=
odd_sum
+
even_sum
check_digit
=
(
10
-
(
total
%
10
))
%
10
return
data
+
str
(
check_digit
)
def
format_bet_id_for_barcode
(
bet_uuid
:
str
,
standard
:
str
)
->
str
:
def
format_bet_id_for_barcode
(
bet_uuid
:
str
,
standard
:
str
)
->
str
:
"""
"""
Format bet UUID for specific barcode standard
Format bet UUID for specific barcode standard
...
@@ -228,17 +344,21 @@ def format_bet_id_for_barcode(bet_uuid: str, standard: str) -> str:
...
@@ -228,17 +344,21 @@ def format_bet_id_for_barcode(bet_uuid: str, standard: str) -> str:
numeric_hash
=
str
(
int
(
hash_obj
.
hexdigest
()[:
12
],
16
))
numeric_hash
=
str
(
int
(
hash_obj
.
hexdigest
()[:
12
],
16
))
if
standard
==
'ean13'
:
if
standard
==
'ean13'
:
# EAN-13 needs exactly 12 digits
# EAN-13 needs exactly 12 digits, then we add check digit to make 13
return
numeric_hash
[:
12
]
.
zfill
(
12
)
data_12
=
numeric_hash
[:
12
]
.
zfill
(
12
)
return
calculate_ean13_check_digit
(
data_12
)
elif
standard
==
'ean8'
:
elif
standard
==
'ean8'
:
# EAN-8 needs exactly 7 digits
# EAN-8 needs exactly 7 digits, then we add check digit to make 8
return
numeric_hash
[:
7
]
.
zfill
(
7
)
data_7
=
numeric_hash
[:
7
]
.
zfill
(
7
)
return
calculate_ean8_check_digit
(
data_7
)
elif
standard
==
'upca'
:
elif
standard
==
'upca'
:
# UPC-A needs exactly 11 digits
# UPC-A needs exactly 11 digits, then we add check digit to make 12
return
numeric_hash
[:
11
]
.
zfill
(
11
)
data_11
=
numeric_hash
[:
11
]
.
zfill
(
11
)
return
calculate_upca_check_digit
(
data_11
)
elif
standard
==
'upce'
:
elif
standard
==
'upce'
:
# UPC-E needs exactly 7 digits
# UPC-E needs exactly 7 digits, then we add check digit to make 8
return
numeric_hash
[:
7
]
.
zfill
(
7
)
data_7
=
numeric_hash
[:
7
]
.
zfill
(
7
)
return
calculate_upce_check_digit
(
data_7
)
elif
standard
==
'codabar'
:
elif
standard
==
'codabar'
:
# Codabar can use digits with start/stop characters
# Codabar can use digits with start/stop characters
return
f
"A{numeric_hash[:14]}A"
return
f
"A{numeric_hash[:14]}A"
...
...
mbetterclient/web_dashboard/routes.py
View file @
262296b4
...
@@ -33,15 +33,20 @@ def get_api_auth_decorator(require_admin=False):
...
@@ -33,15 +33,20 @@ def get_api_auth_decorator(require_admin=False):
from
functools
import
wraps
from
functools
import
wraps
@
wraps
(
func
)
@
wraps
(
func
)
def
decorated_function
(
*
args
,
**
kwargs
):
def
decorated_function
(
*
args
,
**
kwargs
):
from
flask
import
request
from
flask_login
import
current_user
# Get auth_manager from blueprint context (set during app initialization)
# Get auth_manager from blueprint context (set during app initialization)
auth_manager
=
getattr
(
api_bp
,
'auth_manager'
,
None
)
auth_manager
=
getattr
(
api_bp
,
'auth_manager'
,
None
)
if
auth_manager
:
if
auth_manager
:
# Use the auth manager's require_auth method
# Check for Bearer token authentication first
auth_header
=
request
.
headers
.
get
(
'Authorization'
)
if
auth_header
and
auth_header
.
startswith
(
'Bearer '
):
# Use the auth manager's require_auth method for Bearer tokens
if
require_admin
:
if
require_admin
:
# Check if user is admin after authentication
# Check if user is admin after authentication
@
auth_manager
.
require_auth
@
auth_manager
.
require_auth
def
admin_check
(
*
args
,
**
kwargs
):
def
admin_check
(
*
args
,
**
kwargs
):
from
flask
import
request
if
not
hasattr
(
request
,
'current_user'
):
if
not
hasattr
(
request
,
'current_user'
):
return
jsonify
({
'error'
:
'Authentication required'
}),
401
return
jsonify
({
'error'
:
'Authentication required'
}),
401
...
@@ -55,6 +60,28 @@ def get_api_auth_decorator(require_admin=False):
...
@@ -55,6 +60,28 @@ def get_api_auth_decorator(require_admin=False):
return
admin_check
(
*
args
,
**
kwargs
)
return
admin_check
(
*
args
,
**
kwargs
)
else
:
else
:
return
auth_manager
.
require_auth
(
func
)(
*
args
,
**
kwargs
)
return
auth_manager
.
require_auth
(
func
)(
*
args
,
**
kwargs
)
else
:
# No Bearer token - check for web session authentication
if
current_user
.
is_authenticated
:
# Set current_user in request for consistency
request
.
current_user
=
{
'user_id'
:
current_user
.
id
,
'username'
:
current_user
.
username
,
'is_admin'
:
current_user
.
is_admin
,
'role'
:
getattr
(
current_user
,
'role'
,
'normal'
)
}
# Check admin requirement for web session auth
if
require_admin
:
user_role
=
getattr
(
current_user
,
'role'
,
'normal'
)
is_admin
=
getattr
(
current_user
,
'is_admin'
,
False
)
if
user_role
!=
'admin'
and
not
is_admin
:
return
jsonify
({
'error'
:
'Admin access required'
}),
403
return
func
(
*
args
,
**
kwargs
)
else
:
return
jsonify
({
'error'
:
'Authentication required'
}),
401
else
:
else
:
# Fallback to login_required if auth_manager not available
# Fallback to login_required if auth_manager not available
return
login_required
(
func
)(
*
args
,
**
kwargs
)
return
login_required
(
func
)(
*
args
,
**
kwargs
)
...
@@ -246,7 +273,7 @@ def bet_details(bet_id):
...
@@ -246,7 +273,7 @@ def bet_details(bet_id):
has_pending
=
True
has_pending
=
True
elif
detail
.
result
in
[
'won'
,
'win'
]:
elif
detail
.
result
in
[
'won'
,
'win'
]:
results
[
'won'
]
+=
1
results
[
'won'
]
+=
1
results
[
'winnings'
]
+=
float
(
detail
.
amount
)
*
2
# Assume 2x payout for simplicity
results
[
'winnings'
]
+=
float
(
detail
.
amount
)
*
float
(
odds
)
# Use actual odds
elif
detail
.
result
==
'lost'
:
elif
detail
.
result
==
'lost'
:
results
[
'lost'
]
+=
1
results
[
'lost'
]
+=
1
elif
detail
.
result
==
'cancelled'
:
elif
detail
.
result
==
'cancelled'
:
...
@@ -705,7 +732,7 @@ def cashier_bet_details(bet_id):
...
@@ -705,7 +732,7 @@ def cashier_bet_details(bet_id):
has_pending
=
True
has_pending
=
True
elif
detail
.
result
in
[
'won'
,
'win'
]:
elif
detail
.
result
in
[
'won'
,
'win'
]:
results
[
'won'
]
+=
1
results
[
'won'
]
+=
1
results
[
'winnings'
]
+=
float
(
detail
.
amount
)
*
2
# Assume 2x payout for simplicity
results
[
'winnings'
]
+=
float
(
detail
.
amount
)
*
float
(
odds
)
# Use actual odds
elif
detail
.
result
==
'lost'
:
elif
detail
.
result
==
'lost'
:
results
[
'lost'
]
+=
1
results
[
'lost'
]
+=
1
elif
detail
.
result
==
'cancelled'
:
elif
detail
.
result
==
'cancelled'
:
...
@@ -872,10 +899,6 @@ def change_password():
...
@@ -872,10 +899,6 @@ def change_password():
def
statistics
():
def
statistics
():
"""Statistics dashboard page"""
"""Statistics dashboard page"""
try
:
try
:
if
not
current_user
.
is_admin
:
flash
(
"Admin access required"
,
"error"
)
return
redirect
(
url_for
(
'main.index'
))
return
render_template
(
'dashboard/statistics.html'
,
return
render_template
(
'dashboard/statistics.html'
,
user
=
current_user
,
user
=
current_user
,
page_title
=
"Statistics"
)
page_title
=
"Statistics"
)
...
@@ -4524,6 +4547,45 @@ def get_cashier_bet_details(bet_id):
...
@@ -4524,6 +4547,45 @@ def get_cashier_bet_details(bet_id):
total_amount
=
sum
(
float
(
detail
.
amount
)
for
detail
in
bet_details
)
total_amount
=
sum
(
float
(
detail
.
amount
)
for
detail
in
bet_details
)
bet_data
[
'total_amount'
]
=
total_amount
bet_data
[
'total_amount'
]
=
total_amount
# Calculate overall bet status and results
results
=
{
'pending'
:
0
,
'won'
:
0
,
'lost'
:
0
,
'cancelled'
:
0
,
'winnings'
:
0.0
}
overall_status
=
'pending'
for
detail
in
bet_details
:
if
detail
.
result
==
'pending'
:
results
[
'pending'
]
+=
1
elif
detail
.
result
in
[
'won'
,
'win'
]:
results
[
'won'
]
+=
1
# Get odds for this outcome
odds
=
0.0
match
=
session
.
query
(
MatchModel
)
.
filter_by
(
id
=
detail
.
match_id
)
.
first
()
if
match
:
outcomes_dict
=
match
.
get_outcomes_dict
()
odds
=
outcomes_dict
.
get
(
detail
.
outcome
,
0.0
)
results
[
'winnings'
]
+=
float
(
detail
.
amount
)
*
float
(
odds
)
elif
detail
.
result
==
'lost'
:
results
[
'lost'
]
+=
1
elif
detail
.
result
==
'cancelled'
:
results
[
'cancelled'
]
+=
1
# Determine overall status
if
results
[
'pending'
]
==
0
:
if
results
[
'won'
]
>
0
and
results
[
'lost'
]
==
0
:
overall_status
=
'won'
elif
results
[
'lost'
]
>
0
:
overall_status
=
'lost'
elif
results
[
'cancelled'
]
>
0
:
overall_status
=
'cancelled'
bet_data
[
'overall_status'
]
=
overall_status
bet_data
[
'results'
]
=
results
return
jsonify
({
return
jsonify
({
"success"
:
True
,
"success"
:
True
,
"bet"
:
bet_data
"bet"
:
bet_data
...
@@ -4941,7 +5003,13 @@ def verify_barcode():
...
@@ -4941,7 +5003,13 @@ def verify_barcode():
results
[
'pending'
]
+=
1
results
[
'pending'
]
+=
1
elif
detail
.
result
in
[
'won'
,
'win'
]:
elif
detail
.
result
in
[
'won'
,
'win'
]:
results
[
'won'
]
+=
1
results
[
'won'
]
+=
1
results
[
'winnings'
]
+=
float
(
detail
.
amount
)
*
2
# Assume 2x payout
# Get odds for this outcome
odds
=
0.0
match
=
session
.
query
(
MatchModel
)
.
filter_by
(
id
=
detail
.
match_id
)
.
first
()
if
match
:
outcomes_dict
=
match
.
get_outcomes_dict
()
odds
=
outcomes_dict
.
get
(
detail
.
outcome
,
0.0
)
results
[
'winnings'
]
+=
float
(
detail
.
amount
)
*
float
(
odds
)
elif
detail
.
result
==
'lost'
:
elif
detail
.
result
==
'lost'
:
results
[
'lost'
]
+=
1
results
[
'lost'
]
+=
1
elif
detail
.
result
==
'cancelled'
:
elif
detail
.
result
==
'cancelled'
:
...
...
mbetterclient/web_dashboard/templates/base.html
View file @
262296b4
...
@@ -127,6 +127,12 @@
...
@@ -127,6 +127,12 @@
<i
class=
"fas fa-key me-1"
></i>
API Tokens
<i
class=
"fas fa-key me-1"
></i>
API Tokens
</a>
</a>
</li>
</li>
<li
class=
"nav-item"
>
<a
class=
"nav-link {% if request.endpoint == 'main.statistics' %}active{% endif %}"
href=
"{{ url_for('main.statistics') }}"
>
<i
class=
"fas fa-chart-bar me-1"
></i>
Statistics
</a>
</li>
{% if current_user.is_admin %}
{% if current_user.is_admin %}
<li
class=
"nav-item dropdown"
>
<li
class=
"nav-item dropdown"
>
<a
class=
"nav-link dropdown-toggle"
href=
"#"
role=
"button"
data-bs-toggle=
"dropdown"
>
<a
class=
"nav-link dropdown-toggle"
href=
"#"
role=
"button"
data-bs-toggle=
"dropdown"
>
...
@@ -142,9 +148,6 @@
...
@@ -142,9 +148,6 @@
<li><a
class=
"dropdown-item"
href=
"{{ url_for('main.logs') }}"
>
<li><a
class=
"dropdown-item"
href=
"{{ url_for('main.logs') }}"
>
<i
class=
"fas fa-file-alt me-1"
></i>
Logs
<i
class=
"fas fa-file-alt me-1"
></i>
Logs
</a></li>
</a></li>
<li><a
class=
"dropdown-item"
href=
"{{ url_for('main.statistics') }}"
>
<i
class=
"fas fa-chart-bar me-1"
></i>
Statistics
</a></li>
</ul>
</ul>
</li>
</li>
{% endif %}
{% endif %}
...
...
test_auth_verification.py
0 → 100644
View file @
262296b4
#!/usr/bin/env python3
"""
Test script to verify authentication system works correctly
"""
import
sys
import
os
from
pathlib
import
Path
# Add the project root to Python path
project_root
=
Path
(
__file__
)
.
parent
sys
.
path
.
insert
(
0
,
str
(
project_root
))
def
test_auth_imports
():
"""Test that authentication modules can be imported"""
try
:
from
mbetterclient.web_dashboard.auth
import
AuthManager
,
AuthenticatedUser
from
mbetterclient.web_dashboard.routes
import
get_api_auth_decorator
print
(
"✓ Authentication modules imported successfully"
)
return
True
except
ImportError
as
e
:
print
(
f
"✗ Failed to import authentication modules: {e}"
)
return
False
def
test_auth_manager_creation
():
"""Test that AuthManager can be created"""
try
:
from
flask
import
Flask
from
mbetterclient.web_dashboard.auth
import
AuthManager
# Create a minimal Flask app for testing
app
=
Flask
(
__name__
)
app
.
config
[
'SECRET_KEY'
]
=
'test_secret_key'
app
.
config
[
'JWT_SECRET_KEY'
]
=
'test_jwt_secret'
# Mock database manager
class
MockDBManager
:
def
get_user_by_username
(
self
,
username
):
return
None
def
get_user_by_id
(
self
,
user_id
):
return
None
db_manager
=
MockDBManager
()
auth_manager
=
AuthManager
(
db_manager
,
app
)
print
(
"✓ AuthManager created successfully"
)
return
True
except
Exception
as
e
:
print
(
f
"✗ Failed to create AuthManager: {e}"
)
return
False
def
test_decorator_creation
():
"""Test that API auth decorator can be created"""
try
:
from
mbetterclient.web_dashboard.routes
import
get_api_auth_decorator
# Test creating decorators
normal_decorator
=
get_api_auth_decorator
()
admin_decorator
=
get_api_auth_decorator
(
require_admin
=
True
)
print
(
"✓ API auth decorators created successfully"
)
return
True
except
Exception
as
e
:
print
(
f
"✗ Failed to create API auth decorators: {e}"
)
return
False
def
test_authenticated_user_creation
():
"""Test that AuthenticatedUser can be created"""
try
:
from
mbetterclient.web_dashboard.auth
import
AuthenticatedUser
user
=
AuthenticatedUser
(
user_id
=
1
,
username
=
"testuser"
,
email
=
"test@example.com"
,
is_admin
=
False
,
role
=
"normal"
)
# Test properties
assert
user
.
id
==
1
assert
user
.
username
==
"testuser"
assert
user
.
email
==
"test@example.com"
assert
user
.
is_admin
==
False
assert
user
.
role
==
"normal"
assert
user
.
is_authenticated
==
True
assert
user
.
is_active
==
True
assert
user
.
is_anonymous
==
False
# Test helper methods
assert
user
.
is_admin_user
()
==
False
assert
user
.
is_cashier_user
()
==
False
assert
user
.
is_normal_user
()
==
True
print
(
"✓ AuthenticatedUser created and tested successfully"
)
return
True
except
Exception
as
e
:
print
(
f
"✗ Failed to create/test AuthenticatedUser: {e}"
)
return
False
def
test_role_based_access
():
"""Test role-based access control logic"""
try
:
from
mbetterclient.web_dashboard.auth
import
AuthenticatedUser
# Test admin user
admin_user
=
AuthenticatedUser
(
user_id
=
1
,
username
=
"admin"
,
email
=
"admin@example.com"
,
is_admin
=
True
,
role
=
"admin"
)
# Test cashier user
cashier_user
=
AuthenticatedUser
(
user_id
=
2
,
username
=
"cashier"
,
email
=
"cashier@example.com"
,
is_admin
=
False
,
role
=
"cashier"
)
# Test normal user
normal_user
=
AuthenticatedUser
(
user_id
=
3
,
username
=
"normal"
,
email
=
"normal@example.com"
,
is_admin
=
False
,
role
=
"normal"
)
# Test admin access
assert
admin_user
.
is_admin_user
()
==
True
assert
cashier_user
.
is_admin_user
()
==
False
assert
normal_user
.
is_admin_user
()
==
False
# Test cashier access
assert
admin_user
.
is_cashier_user
()
==
False
# Admin is not cashier
assert
cashier_user
.
is_cashier_user
()
==
True
assert
normal_user
.
is_cashier_user
()
==
False
# Test normal access
assert
admin_user
.
is_normal_user
()
==
False
# Admin is not normal
assert
cashier_user
.
is_normal_user
()
==
False
# Cashier is not normal
assert
normal_user
.
is_normal_user
()
==
True
print
(
"✓ Role-based access control tested successfully"
)
return
True
except
Exception
as
e
:
print
(
f
"✗ Failed to test role-based access control: {e}"
)
return
False
def
main
():
"""Run all authentication tests"""
print
(
"MbetterClient Authentication System Verification"
)
print
(
"="
*
50
)
tests
=
[
test_auth_imports
,
test_auth_manager_creation
,
test_decorator_creation
,
test_authenticated_user_creation
,
test_role_based_access
,
]
passed
=
0
total
=
len
(
tests
)
for
test
in
tests
:
try
:
if
test
():
passed
+=
1
print
()
except
Exception
as
e
:
print
(
f
"✗ Test {test.__name__} failed with exception: {e}"
)
print
()
print
(
"="
*
50
)
if
passed
==
total
:
print
(
f
"✓ All {total} authentication tests passed!"
)
print
(
"
\n
The authentication system appears to be working correctly."
)
print
(
"Key findings:"
)
print
(
"- Authentication modules import successfully"
)
print
(
"- AuthManager can be instantiated"
)
print
(
"- API auth decorators can be created"
)
print
(
"- AuthenticatedUser class works correctly"
)
print
(
"- Role-based access control logic is correct"
)
return
True
else
:
print
(
f
"✗ {total - passed} out of {total} tests failed"
)
return
False
if
__name__
==
"__main__"
:
success
=
main
()
sys
.
exit
(
0
if
success
else
1
)
\ No newline at end of file
test_config_auth.py
0 → 100644
View file @
262296b4
#!/usr/bin/env python3
"""
Test script to verify configuration saving authentication works
"""
import
requests
import
json
import
sys
from
datetime
import
datetime
def
test_config_saving
():
"""Test configuration saving with web session authentication"""
# Test configuration data
test_config
=
{
"app_name"
:
f
"TestApp_{datetime.now().strftime('
%
H
%
M
%
S')}"
,
"log_level"
:
"INFO"
,
"enable_qt"
:
True
}
print
(
"Testing configuration saving with web session authentication..."
)
print
(
f
"Test config: {test_config}"
)
try
:
# This test assumes the server is running on localhost:5000
# In a real test environment, you would need to:
# 1. Start the Flask server
# 2. Log in as an admin user to establish a session
# 3. Make the API call with the session cookie
# For now, we'll just test the endpoint structure
print
(
"Note: This test requires a running server with an active admin session"
)
print
(
"To test manually:"
)
print
(
"1. Start the MbetterClient web server"
)
print
(
"2. Log in as an admin user"
)
print
(
"3. Open browser dev tools and run:"
)
print
()
print
(
"fetch('/api/config/general', {"
)
print
(
" method: 'POST',"
)
print
(
" headers: { 'Content-Type': 'application/json' },"
)
print
(
f
" body: JSON.stringify({test_config})"
)
print
(
"}).then(r => r.json()).then(console.log)"
)
print
()
print
(
"Expected result: { success: true, message: '...' }"
)
print
(
"NOT: { error: 'Authentication required' }"
)
return
True
except
Exception
as
e
:
print
(
f
"Test failed: {e}"
)
return
False
def
test_api_token_auth
():
"""Test that API token authentication still works"""
print
(
"
\n
Testing API token authentication still works..."
)
try
:
# Test the /auth/token endpoint to get a JWT token
# This would require valid credentials
print
(
"Note: API token authentication should still work for external API calls"
)
print
(
"External API consumers should use: Authorization: Bearer <token>"
)
return
True
except
Exception
as
e
:
print
(
f
"API token test failed: {e}"
)
return
False
if
__name__
==
"__main__"
:
print
(
"MbetterClient Configuration Authentication Test"
)
print
(
"="
*
50
)
success
=
True
success
&=
test_config_saving
()
success
&=
test_api_token_auth
()
print
(
"
\n
"
+
"="
*
50
)
if
success
:
print
(
"✓ All tests passed!"
)
print
(
"
\n
The authentication fix should resolve the 'auth required' error"
)
print
(
"when saving configuration from the admin interface."
)
else
:
print
(
"✗ Some tests failed"
)
sys
.
exit
(
1
)
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment