Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions application/single_app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
from route_frontend_feedback import *
from route_frontend_support import *
from route_frontend_notifications import *
from route_frontend_terms_of_use import register_route_frontend_terms_of_use
from route_custom_pages import register_route_custom_pages

from route_backend_chats import *
Expand Down Expand Up @@ -98,6 +99,7 @@
from route_plugin_logging import bpl as plugin_logging_bp
from functions_custom_pages import get_custom_pages_nav
from functions_debug import debug_print
from functions_terms_of_use import has_terms_of_use_acceptance

from opentelemetry.instrumentation.flask import FlaskInstrumentor

Expand Down Expand Up @@ -813,6 +815,74 @@
return any(path.startswith(prefix) for prefix in IDLE_TIMEOUT_EXEMPT_PREFIXES)


TERMS_OF_USE_EXEMPT_PATHS = {
'/login',
'/logout',
'/logout/local',
'/getAToken',

Check warning on line 822 in application/single_app/app.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.
'/getATokenApi',

Check warning on line 823 in application/single_app/app.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.
'/ci-auth/session',
'/auth/teams/token-exchange',

Check warning on line 825 in application/single_app/app.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.
'/terms-of-use',
'/terms-of-use/accept',
'/terms-of-use/decline',
'/robots933456.txt',
'/favicon.ico',
'/acceptable_use_policy.html',

Check warning on line 831 in application/single_app/app.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.
'/external/healthcheck',
'/external/healthcheckz',
}

TERMS_OF_USE_EXEMPT_PREFIXES = (
'/static/',
'/health',
'/api/health',
)


def _is_terms_of_use_exempt(path):
if path in TERMS_OF_USE_EXEMPT_PATHS:
return True
return any(path.startswith(prefix) for prefix in TERMS_OF_USE_EXEMPT_PREFIXES)


@app.before_request
def enforce_terms_of_use():
"""Block authenticated app usage until the current terms of use is accepted."""
if 'user' not in session:
return None
if request.method == 'OPTIONS' or _is_terms_of_use_exempt(request.path):
return None

request_settings = get_request_settings()
user_id = session.get('user', {}).get('oid') or session.get('user', {}).get('sub')
if has_terms_of_use_acceptance(request_settings, user_id=user_id):
return None

terms_url = url_for(
'frontend_terms_of_use.terms_of_use',
next=normalize_path_with_query(request.path, request.query_string),
)
is_api_request = (
request.accept_mimetypes.accept_json
and not request.accept_mimetypes.accept_html
) or request.path.startswith('/api/')
if is_api_request:
return jsonify({
'error': 'terms_of_use_required',
'message': 'Terms of Use acceptance is required before using SimpleChat.',
'terms_url': terms_url,
}), 403
return redirect(terms_url)


def normalize_path_with_query(path, query_string):
query_text = query_string.decode('utf-8', errors='ignore') if isinstance(query_string, bytes) else str(query_string or '')
if not query_text:
return path
return f"{path}?{query_text}"


def maybe_log_authenticated_browser_request():
"""Record throttled login activity for authenticated browser page requests."""
if request.method != 'GET' or request.path.startswith('/api/'):
Expand Down Expand Up @@ -1079,6 +1149,9 @@
# ------------------- User Authentication Routes ---------
register_route_blueprint('frontend_authentication', register_route_frontend_authentication)

# ------------------- Terms of Use Routes --
register_route_blueprint('frontend_terms_of_use', register_route_frontend_terms_of_use)

# ------------------- User Profile Routes ----------------
register_route_blueprint('frontend_profile', register_route_frontend_profile, login_required_blueprint)

Expand Down
5 changes: 4 additions & 1 deletion application/single_app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
EXECUTOR_TYPE = 'thread'
EXECUTOR_MAX_WORKERS = 30
SESSION_TYPE = 'filesystem'
VERSION = "0.250.052"
VERSION = "0.250.057"

SESSION_COOKIE_SAMESITE = os.getenv('SESSION_COOKIE_SAMESITE', 'Lax')
SESSION_COOKIE_HTTPONLY = os.getenv('SESSION_COOKIE_HTTPONLY', 'true').lower() != 'false'
Expand Down Expand Up @@ -255,6 +255,9 @@ def get_allowed_extension_categories(enable_video=False, enable_audio=False):
'/login',
'/logout',
'/logout/local',
'/terms-of-use',
'/terms-of-use/accept',
'/terms-of-use/decline',
'/getAToken',
'/getATokenApi',
'/ci-auth/session',
Expand Down
90 changes: 90 additions & 0 deletions application/single_app/functions_activity_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -1752,6 +1752,96 @@
debug_print(f"⚠️ Warning: Failed to log user agreement acceptance: {str(e)}")


def log_terms_of_use_accepted(
user_id: str,
terms_hash: str,
frequency: str,
source: str,
accepted_date: str,
auth_state: Optional[str] = None,
) -> None:
"""Log when a user accepts the terms of use."""
try:

Check warning on line 1764 in application/single_app/functions_activity_logging.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.
acceptance_record = {
'id': str(uuid.uuid4()),
'user_id': user_id,
'activity_type': 'terms_of_use_accepted',
'timestamp': datetime.utcnow().isoformat(),
'created_at': datetime.utcnow().isoformat(),
'accepted_date': accepted_date,
'terms_hash': terms_hash,
'frequency': frequency,
'source': source,
'auth_state': auth_state or 'authenticated',
}

cosmos_activity_logs_container.create_item(body=acceptance_record)

Check warning on line 1778 in application/single_app/functions_activity_logging.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.
log_event(

Check warning on line 1779 in application/single_app/functions_activity_logging.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.
message=f"Terms of Use accepted: user {user_id}",
extra=acceptance_record,
level=logging.INFO,
)
except Exception as e:

Check warning on line 1784 in application/single_app/functions_activity_logging.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.
log_event(

Check warning on line 1785 in application/single_app/functions_activity_logging.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.
message=f"Error logging terms of use acceptance: {str(e)}",
extra={
'user_id': user_id,
'terms_hash': terms_hash,
'frequency': frequency,
'source': source,
'error': str(e),
},
level=logging.ERROR,
)
debug_print(f"⚠️ Warning: Failed to log terms of use acceptance: {str(e)}")


def log_terms_of_use_declined(
user_id: str,
terms_hash: str,
frequency: str,
source: str,
redirect_url: str,
auth_state: Optional[str] = None,
) -> None:
"""Log when a signed-in user declines the terms of use."""
try:

Check warning on line 1808 in application/single_app/functions_activity_logging.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.
decline_record = {
'id': str(uuid.uuid4()),
'user_id': user_id,
'activity_type': 'terms_of_use_declined',
'timestamp': datetime.utcnow().isoformat(),
'created_at': datetime.utcnow().isoformat(),
'declined_date': datetime.utcnow().strftime('%Y-%m-%d'),
'terms_hash': terms_hash,
'frequency': frequency,
'source': source,
'redirect_url': redirect_url,
'auth_state': auth_state or 'authenticated',
}

cosmos_activity_logs_container.create_item(body=decline_record)
log_event(
message=f"Terms of Use declined: user {user_id}",
extra=decline_record,
level=logging.INFO,
)
except Exception as e:
log_event(
message=f"Error logging terms of use decline: {str(e)}",
extra={
'user_id': user_id,
'terms_hash': terms_hash,
'frequency': frequency,
'source': source,
'redirect_url': redirect_url,
'error': str(e),
},
level=logging.ERROR,
)
debug_print(f"⚠️ Warning: Failed to log terms of use decline: {str(e)}")


def has_user_accepted_agreement_today(
user_id: str,
workspace_type: str,
Expand Down
7 changes: 7 additions & 0 deletions application/single_app/functions_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,13 @@ def get_settings(use_cosmos=False, include_source=False):
'logo_dark_version': 1,
'custom_favicon_base64': '',
'favicon_version': 1,
'enable_terms_of_use': False,
'terms_of_use_title': 'Terms of Use',
'terms_of_use_message': '',
'terms_of_use_frequency': 'once',
'terms_of_use_decline_redirect_url': '/',
'terms_of_use_accept_button_text': 'Accept and continue',
'terms_of_use_decline_button_text': 'Cancel',
'enable_dark_mode_default': False,
'enable_left_nav_default': True,
'release_notifications_registered': False,
Expand Down
Loading
Loading