- Correctie reset password en confirm email adress by adapting the prefixed_url_for to use config setting

- Adaptation of DPA and T&Cs
- Refer to privacy statement as DPA, not a privacy statement
- Startup of enforcing signed DPA and T&Cs
- Adaptation of eveai_chat_client to ensure we retrieve correct DPA & T&Cs
This commit is contained in:
Josako
2025-10-13 14:28:09 +02:00
parent 83272a4e2a
commit 37819cd7e5
35 changed files with 5350 additions and 241 deletions

View File

@@ -1,4 +1,5 @@
from datetime import date from datetime import date
from enum import Enum
from common.extensions import db from common.extensions import db
from flask_security import UserMixin, RoleMixin from flask_security import UserMixin, RoleMixin
@@ -121,7 +122,6 @@ class User(db.Model, UserMixin):
def has_roles(self, *args): def has_roles(self, *args):
return any(role.name in args for role in self.roles) return any(role.name in args for role in self.roles)
class TenantDomain(db.Model): class TenantDomain(db.Model):
__bind_key__ = 'public' __bind_key__ = 'public'
__table_args__ = {'schema': 'public'} __table_args__ = {'schema': 'public'}
@@ -311,6 +311,49 @@ class PartnerTenant(db.Model):
updated_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=True) updated_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=True)
class TenantConsent(db.Model):
__bind_key__ = 'public'
__table_args__ = {'schema': 'public'}
id = db.Column(db.Integer, primary_key=True)
tenant_id = db.Column(db.Integer, db.ForeignKey('public.tenant.id'), nullable=False)
partner_id = db.Column(db.Integer, db.ForeignKey('public.partner.id'), nullable=False)
partner_service_id = db.Column(db.Integer, db.ForeignKey('public.partner_service.id'), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=False)
consent_type = db.Column(db.String(50), nullable=False)
consent_date = db.Column(db.DateTime, nullable=False, server_default=db.func.now())
consent_version = db.Column(db.String(20), nullable=False, default="1.0.0")
consent_data = db.Column(db.JSON, nullable=False)
# Tracking
created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now())
created_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=True)
updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now())
updated_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=True)
class ConsentVersion(db.Model):
__bind_key__ = 'public'
__table_args__ = {'schema': 'public'}
id = db.Column(db.Integer, primary_key=True)
consent_type = db.Column(db.String(50), nullable=False)
consent_version = db.Column(db.String(20), nullable=False)
consent_valid_from = db.Column(db.DateTime, nullable=False, server_default=db.func.now())
consent_valid_to = db.Column(db.DateTime, nullable=True)
# Tracking
created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now())
created_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=True)
updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now())
updated_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=True)
class ConsentStatus(Enum):
CONSENTED = 'CONSENTED'
NOT_CONSENTED = 'NOT_CONSENTED'
RENEWAL_REQUIRED = 'RENEWAL_REQUIRED'
CONSENT_EXPIRED = 'CONSENT_EXPIRED'
UNKNOWN_CONSENT_VERSION = 'UNKNOWN_CONSENT_VERSION'
class SpecialistMagicLinkTenant(db.Model): class SpecialistMagicLinkTenant(db.Model):
__bind_key__ = 'public' __bind_key__ = 'public'
__table_args__ = {'schema': 'public'} __table_args__ = {'schema': 'public'}

View File

@@ -1,10 +1,12 @@
from typing import Dict, List from typing import Dict, List
from flask import session, current_app from flask import session, current_app
from sqlalchemy import desc
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from common.extensions import db, cache_manager from common.extensions import db, cache_manager
from common.models.user import Partner, PartnerTenant, PartnerService, Tenant from common.models.user import Partner, PartnerTenant, PartnerService, Tenant, TenantConsent, ConsentStatus, \
ConsentVersion
from common.utils.eveai_exceptions import EveAINoManagementPartnerService from common.utils.eveai_exceptions import EveAINoManagementPartnerService
from common.utils.model_logging_utils import set_logging_information from common.utils.model_logging_utils import set_logging_information
from datetime import datetime as dt, timezone as tz from datetime import datetime as dt, timezone as tz
@@ -172,4 +174,30 @@ class TenantServices:
except Exception as e: except Exception as e:
current_app.logger.error(f"Error checking specialist type access: {str(e)}") current_app.logger.error(f"Error checking specialist type access: {str(e)}")
return False return False
@staticmethod
def get_consent_status(tenant_id: int) -> ConsentStatus:
cts = current_app.config.get("CONSENT_TYPES")
status = ConsentStatus.CONSENTED
for ct in cts:
consent = (TenantConsent.query.filter_by(tenant_id=tenant_id, consent_type=ct)
.order_by(desc(TenantConsent.id))
.first())
if not consent:
status = ConsentStatus.NOT_CONSENTED
break
cv = ConsentVersion.query.filter_by(consent_type=ct, consent_version=consent.consent_version).first()
if not cv:
current_app.logger.error(f"Consent version {consent.consent_version} not found checking tenant {tenant_id}")
status = ConsentStatus.UNKNOWN_CONSENT_VERSION
break
if cv.consent_valid_to:
if cv.consent_valid_to.date() >= dt.now(tz.utc).date():
status = ConsentStatus.RENEWAL_REQUIRED
break
else:
status = ConsentStatus.NOT_CONSENTED
break
return status

View File

@@ -1,14 +1,39 @@
from flask import request, url_for from flask import request, url_for, current_app
from urllib.parse import urlsplit, urlunsplit from urllib.parse import urlsplit, urlunsplit
import re import re
VISIBLE_PREFIXES = ('/admin', '/api', '/chat-client') VISIBLE_PREFIXES = ('/admin', '/api', '/chat-client')
def _normalize_prefix(raw_prefix: str) -> str:
"""Normalize config prefix to internal form '/admin' or '' if not set."""
if not raw_prefix:
return ''
s = str(raw_prefix).strip()
if not s:
return ''
# remove leading/trailing slashes, then add single leading slash
s = s.strip('/')
if not s:
return ''
return f"/{s}"
def _get_config_prefix() -> str:
"""Return normalized prefix from config EVEAI_APP_PREFIX (config-first)."""
try:
cfg_val = (current_app.config.get('EVEAI_APP_PREFIX') if current_app else None)
return _normalize_prefix(cfg_val)
except Exception:
return ''
def _derive_visible_prefix(): def _derive_visible_prefix():
# 1) Edge-provided header (beste en meest expliciete bron) # 1) Edge-provided header (beste en meest expliciete bron)
xfp = request.headers.get('X-Forwarded-Prefix') xfp = request.headers.get('X-Forwarded-Prefix')
if xfp and any(xfp.startswith(p) for p in VISIBLE_PREFIXES): current_app.logger.debug(f"X-Forwarded-Prefix: {xfp}")
return xfp.rstrip('/') if xfp and any(str(xfp).startswith(p) for p in VISIBLE_PREFIXES):
return str(xfp).rstrip('/')
# 2) Referer fallback: haal het top-level segment uit de Referer path # 2) Referer fallback: haal het top-level segment uit de Referer path
ref = request.headers.get('Referer') or '' ref = request.headers.get('Referer') or ''
@@ -24,13 +49,31 @@ def _derive_visible_prefix():
return '' return ''
def _visible_prefix_for_runtime() -> str:
"""Decide which prefix to use at runtime.
Priority: config EVEAI_APP_PREFIX; optional dynamic fallback if enabled.
"""
cfg_prefix = _get_config_prefix()
if cfg_prefix:
current_app.logger.debug(f"prefixed_url_for: using config prefix: {cfg_prefix}")
return cfg_prefix
# Optional dynamic fallback
use_fallback = bool(current_app.config.get('EVEAI_USE_DYNAMIC_PREFIX_FALLBACK', False)) if current_app else False
if use_fallback:
dyn = _derive_visible_prefix()
current_app.logger.debug(f"prefixed_url_for: using dynamic fallback prefix: {dyn}")
return dyn
current_app.logger.debug("prefixed_url_for: no prefix configured, no fallback enabled")
return ''
def prefixed_url_for(endpoint, **values): def prefixed_url_for(endpoint, **values):
""" """
Gedrag: Gedrag:
- Default (_external=False, for_redirect=False): retourneer relatief pad (zonder leading '/') - Default (_external=False, for_redirect=False): retourneer relatief pad (zonder leading '/')
voor templates/JS. De dynamische <base> zorgt voor correcte resolutie onder het zichtbare prefix. voor templates/JS. De dynamische <base> zorgt voor correcte resolutie onder het zichtbare prefix.
- _external=True: bouw absolute URL (schema/host). Als X-Forwarded-Prefix aanwezig is, - _external=True: bouw absolute URL (schema/host). Pad wordt geprefixt met config prefix (indien gezet),
prefixeer de path daarmee (handig voor e-mails/deeplinks). of optioneel met dynamische fallback wanneer geactiveerd.
- for_redirect=True: geef root-absoluut pad inclusief zichtbaar top-prefix, geschikt - for_redirect=True: geef root-absoluut pad inclusief zichtbaar top-prefix, geschikt
voor HTTP Location headers. Backwards compat: _as_location=True wordt behandeld als for_redirect. voor HTTP Location headers. Backwards compat: _as_location=True wordt behandeld als for_redirect.
""" """
@@ -46,16 +89,20 @@ def prefixed_url_for(endpoint, **values):
if external: if external:
scheme = request.headers.get('X-Forwarded-Proto', request.scheme) scheme = request.headers.get('X-Forwarded-Proto', request.scheme)
host = request.headers.get('Host', request.host) host = request.headers.get('Host', request.host)
xfp = request.headers.get('X-Forwarded-Prefix', '') or '' visible_prefix = _visible_prefix_for_runtime()
new_path = (xfp.rstrip('/') + path) if (xfp and not path.startswith(xfp)) else path new_path = (visible_prefix.rstrip('/') + path) if (visible_prefix and not path.startswith(visible_prefix)) else path
current_app.logger.debug(f"prefixed_url_for external: {scheme}://{host}{new_path}")
return urlunsplit((scheme, host, new_path, query, fragment)) return urlunsplit((scheme, host, new_path, query, fragment))
if for_redirect: if for_redirect:
visible_prefix = _derive_visible_prefix() visible_prefix = _visible_prefix_for_runtime()
if visible_prefix and not path.startswith(visible_prefix): if visible_prefix and not path.startswith(visible_prefix):
return f"{visible_prefix}{path}" composed = f"{visible_prefix}{path}"
# root-absoluut pad, zonder prefix als onbekend current_app.logger.debug(f"prefixed_url_for redirect: {composed}")
return composed
current_app.logger.debug(f"prefixed_url_for redirect (no prefix): {path}")
return path return path
# Default: relatief pad # Default: relatief pad (zonder leading '/')
return path[1:] if path.startswith('/') else path rel = path[1:] if path.startswith('/') else path
return rel

View File

@@ -36,7 +36,7 @@ def send_confirmation_email(user):
try: try:
send_email(user.email, f"{user.first_name} {user.last_name}", "Confirm your email", html) send_email(user.email, f"{user.first_name} {user.last_name}", "Confirm your email", html)
current_app.logger.info(f'Confirmation email sent to {user.email}') current_app.logger.info(f'Confirmation email sent to {user.email} with url: {confirm_url}')
except Exception as e: except Exception as e:
current_app.logger.error(f'Failed to send confirmation email to {user.email}. Error: {str(e)}') current_app.logger.error(f'Failed to send confirmation email to {user.email}. Error: {str(e)}')
raise raise
@@ -51,7 +51,7 @@ def send_reset_email(user):
try: try:
send_email(user.email, f"{user.first_name} {user.last_name}", subject, html) send_email(user.email, f"{user.first_name} {user.last_name}", subject, html)
current_app.logger.info(f'Reset email sent to {user.email}') current_app.logger.info(f'Reset email sent to {user.email} with url: {reset_url}')
except Exception as e: except Exception as e:
current_app.logger.error(f'Failed to send reset email to {user.email}. Error: {str(e)}') current_app.logger.error(f'Failed to send reset email to {user.email}. Error: {str(e)}')
raise raise

View File

@@ -310,15 +310,12 @@ class Config(object):
# API Encryption ------------------------------------------------------------------------------ # API Encryption ------------------------------------------------------------------------------
API_ENCRYPTION_KEY = environ.get('API_ENCRYPTION_KEY') API_ENCRYPTION_KEY = environ.get('API_ENCRYPTION_KEY')
# Email settings for API key notifications # Email settings for API key notifications ----------------------------------------------------
PROMOTIONAL_IMAGE_URL = 'https://askeveai.com/wp-content/uploads/2024/07/Evie-Call-scaled.jpg' # Replace with your actual URL PROMOTIONAL_IMAGE_URL = 'https://askeveai.com/wp-content/uploads/2024/07/Evie-Call-scaled.jpg' # Replace with your actual URL
# Langsmith settings # Type Definitions ----------------------------------------------------------------------------
LANGCHAIN_TRACING_V2 = True
LANGCHAIN_ENDPOINT = 'https://api.smith.langchain.com'
LANGCHAIN_PROJECT = "eveai"
TENANT_TYPES = ['Active', 'Demo', 'Inactive', 'Test'] TENANT_TYPES = ['Active', 'Demo', 'Inactive', 'Test']
CONSENT_TYPES = ["Data Privacy Agreement", "Terms & Conditions"]
# The maximum number of seconds allowed for audio compression (to save resources) # The maximum number of seconds allowed for audio compression (to save resources)
MAX_COMPRESSION_DURATION = 60*10 # 10 minutes MAX_COMPRESSION_DURATION = 60*10 # 10 minutes
@@ -351,7 +348,7 @@ class Config(object):
# Entitlement Constants # Entitlement Constants
ENTITLEMENTS_MAX_PENDING_DAYS = 5 # Defines the maximum number of days a pending entitlement can be active ENTITLEMENTS_MAX_PENDING_DAYS = 5 # Defines the maximum number of days a pending entitlement can be active
# Content Directory for static content like the changelog, terms & conditions, privacy statement, ... # Content Directory for static content like the changelog, terms & conditions, dpa statement, ...
CONTENT_DIR = '/app/content' CONTENT_DIR = '/app/content'
# Ensure health check endpoints are exempt from CSRF protection # Ensure health check endpoints are exempt from CSRF protection
@@ -361,6 +358,12 @@ class Config(object):
] ]
SECURITY_LOGIN_WITHOUT_VIEWS = True # Dit voorkomt automatische redirects SECURITY_LOGIN_WITHOUT_VIEWS = True # Dit voorkomt automatische redirects
# Define the nginx prefix used for the specific apps
CHAT_CLIENT_PREFIX = 'chat-client/chat/'
EVEAI_APP_PREFIX = 'admin/'
# Whether to use dynamic fallback (X-Forwarded-Prefix/Referer) when EVEAI_APP_PREFIX is empty
EVEAI_USE_DYNAMIC_PREFIX_FALLBACK = False
class DevConfig(Config): class DevConfig(Config):
DEVELOPMENT = True DEVELOPMENT = True
@@ -368,9 +371,6 @@ class DevConfig(Config):
FLASK_DEBUG = True FLASK_DEBUG = True
EXPLAIN_TEMPLATE_LOADING = False EXPLAIN_TEMPLATE_LOADING = False
# Define the nginx prefix used for the specific apps
CHAT_CLIENT_PREFIX = 'chat-client/chat/'
# Define the static path # Define the static path
STATIC_URL = None STATIC_URL = None
@@ -394,9 +394,6 @@ class TestConfig(Config):
FLASK_DEBUG = True FLASK_DEBUG = True
EXPLAIN_TEMPLATE_LOADING = False EXPLAIN_TEMPLATE_LOADING = False
# Define the nginx prefix used for the specific apps
CHAT_CLIENT_PREFIX = 'chat-client/chat/'
# Define the static path # Define the static path
STATIC_URL = None STATIC_URL = None
@@ -420,9 +417,6 @@ class StagingConfig(Config):
FLASK_DEBUG = True FLASK_DEBUG = True
EXPLAIN_TEMPLATE_LOADING = False EXPLAIN_TEMPLATE_LOADING = False
# Define the nginx prefix used for the specific apps
CHAT_CLIENT_PREFIX = 'chat-client/chat/'
# Define the static path # Define the static path
STATIC_URL = 'https://evie-staging-static.askeveai.com/' STATIC_URL = 'https://evie-staging-static.askeveai.com/'

View File

@@ -26,7 +26,7 @@ fields:
required: true required: true
meta: meta:
kind: "consent" kind: "consent"
consentRich: "Ik Agree with the <terms>Terms and Conditions</terms> and the <privacy>Privacy Statement</privacy> of Ask Eve AI" consentRich: "Ik Agree with the <terms>Terms and Conditions</terms> and the <dpa>Privacy Statement</dpa> of Ask Eve AI"
ariaPrivacy: "Open privacyverklaring in a modal dialog" ariaPrivacy: "Open privacyverklaring in a modal dialog"
ariaTerms: "Open algemene voorwaarden in a modal dialog" ariaTerms: "Open algemene voorwaarden in a modal dialog"
metadata: metadata:

View File

@@ -1,6 +1,6 @@
{ {
"dist/chat-client.js": "dist/chat-client.421bb8ee.js", "dist/chat-client.js": "dist/chat-client.59b28883.js",
"dist/chat-client.css": "dist/chat-client.23ac6be5.css", "dist/chat-client.css": "dist/chat-client.79757200.css",
"dist/main.js": "dist/main.f3dde0f6.js", "dist/main.js": "dist/main.f3dde0f6.js",
"dist/main.css": "dist/main.c40e57ad.css" "dist/main.css": "dist/main.c40e57ad.css"
} }

View File

@@ -0,0 +1,671 @@
# Data Protection Impact Assessment (DPIA) Template
## Ask Eve AI
**Date of Assessment**: [Date]
**Assessed By**: [Name, Role]
**Review Date**: [Date - recommend annual review]
---
## 1. Executive Summary
| Field | Details |
|-------|---------|
| **Processing Activity Name** | [e.g., "Job Candidate Assessment Specialist"] |
| **Brief Description** | [1-2 sentence summary] |
| **Risk Level** | ☐ Low ☐ Medium ☐ High |
| **DPIA Required?** | ☐ Yes ☐ No |
| **Status** | ☐ Draft ☐ Under Review ☐ Approved ☐ Requires Revision |
---
## 2. Description of the Processing
### 2.1 Nature of the Processing
**What Personal Data will be processed?**
- [ ] Contact information (name, email, phone)
- [ ] Identification data (ID numbers, passport)
- [ ] Professional data (CV, work history, qualifications)
- [ ] Assessment results or scores
- [ ] Communication records
- [ ] Behavioral data (how users interact with the system)
- [ ] Technical data (IP addresses, device information)
- [ ] Other: _______________
**Categories of Data Subjects:**
- [ ] Job applicants/candidates
- [ ] Employees
- [ ] Customers
- [ ] End users/consumers
- [ ] Other: _______________
**Volume of Data Subjects:**
- [ ] < 100
- [ ] 100-1,000
- [ ] 1,000-10,000
- [ ] > 10,000
### 2.2 Scope of the Processing
**What is the purpose of the processing?**
[Describe the specific business purpose, e.g., "To assess job candidates' suitability for specific roles by analyzing their responses to standardized questions"]
**How will the data be collected?**
- [ ] Directly from data subjects (forms, interviews)
- [ ] From third parties (recruiters, references)
- [ ] Automated collection (web forms, chatbots)
- [ ] Other: _______________
**Where will data be stored?**
- [ ] EU (specify: France - Scaleway)
- [ ] Non-EU (specify and justify): _______________
### 2.3 Context of the Processing
**Is this processing new or existing?**
- [ ] New processing activity
- [ ] Modification of existing processing
- [ ] Existing processing (periodic review)
**Who has access to the Personal Data?**
- [ ] Ask Eve AI employees (specify roles): _______________
- [ ] Customer/Tenant employees
- [ ] Partners (specify): _______________
- [ ] Sub-Processors (list): _______________
- [ ] Other: _______________
**How long will data be retained?**
[Specify retention period and justification, e.g., "Candidate data retained for 12 months to comply with recruitment record-keeping requirements"]
---
## 3. Necessity and Proportionality Assessment
### 3.1 Lawful Basis
**What is the lawful basis for processing? (Article 6 GDPR)**
- [ ] **Consent** - Data subject has given explicit consent
- [ ] **Contract** - Processing necessary for contract performance
- [ ] **Legal obligation** - Required by law
- [ ] **Vital interests** - Necessary to protect someone's life
- [ ] **Public task** - Performing a public interest task
- [ ] **Legitimate interests** - Necessary for legitimate interests (requires balancing test)
**Justification:**
[Explain why this lawful basis applies]
### 3.2 Special Categories of Data (if applicable)
**Does the processing involve special categories of data? (Article 9 GDPR)**
- [ ] No
- [ ] Yes - racial or ethnic origin
- [ ] Yes - political opinions
- [ ] Yes - religious or philosophical beliefs
- [ ] Yes - trade union membership
- [ ] Yes - genetic data
- [ ] Yes - biometric data for identification
- [ ] Yes - health data
- [ ] Yes - sex life or sexual orientation data
**If yes, what is the additional lawful basis?**
[Article 9(2) provides specific conditions - specify which applies]
### 3.3 Automated Decision-Making
**Does the processing involve automated decision-making or profiling?**
- [ ] No
- [ ] Yes - automated decision-making WITH human oversight
- [ ] Yes - fully automated decision-making (no human intervention)
**If yes:**
**Does it produce legal effects or similarly significant effects?**
- [ ] No
- [ ] Yes (explain): _______________
**What safeguards are in place?**
- [ ] Right to obtain human intervention
- [ ] Right to express point of view
- [ ] Right to contest the decision
- [ ] Regular accuracy reviews
- [ ] Transparency about logic involved
- [ ] Other: _______________
### 3.4 Necessity Test
**Is the processing necessary to achieve the stated purpose?**
☐ Yes ☐ No
**Justification:**
[Explain why this specific processing is necessary and whether less intrusive alternatives were considered]
**Could the purpose be achieved with less data or through other means?**
☐ Yes (explain why not pursued): _______________
☐ No
### 3.5 Proportionality Test
**Is the processing proportionate to the purpose?**
☐ Yes ☐ No
**Data Minimization:**
- Are you collecting only the minimum data necessary? ☐ Yes ☐ No
- Have you considered pseudonymization or anonymization? ☐ Yes ☐ No ☐ N/A
- Can data be aggregated instead of individual records? ☐ Yes ☐ No ☐ N/A
**Storage Limitation:**
- Is the retention period justified and documented? ☐ Yes ☐ No
- Is there an automated deletion process? ☐ Yes ☐ No ☐ Planned
---
## 4. Stakeholder Consultation
### 4.1 Data Subject Consultation
**Have data subjects been consulted about this processing?**
☐ Yes ☐ No ☐ Not required
**If yes, how were they consulted?**
[Describe consultation method: surveys, focus groups, user research, etc.]
**Key concerns raised by data subjects:**
[List any concerns and how they were addressed]
### 4.2 DPO or Security Contact Consultation
**Has the DPO or security contact been consulted?**
☐ Yes ☐ No ☐ N/A (no formal DPO)
**Comments from DPO/Security Contact:**
[Record any recommendations or concerns]
---
## 5. Risk Assessment
### 5.1 Risk Identification
For each risk, assess:
- **Likelihood**: Negligible / Low / Medium / High
- **Severity**: Negligible / Low / Medium / High
- **Overall Risk**: Low / Medium / High / Very High
**Risk 1: Unauthorized Access or Data Breach**
**Description**: Personal data could be accessed by unauthorized parties due to security vulnerabilities.
| Assessment | Rating |
|------------|--------|
| Likelihood | ☐ Negligible ☐ Low ☐ Medium ☐ High |
| Severity (if occurs) | ☐ Negligible ☐ Low ☐ Medium ☐ High |
| **Overall Risk** | ☐ Low ☐ Medium ☐ High ☐ Very High |
**Risk 2: Discrimination or Bias in Automated Decisions**
**Description**: Automated processing could result in discriminatory outcomes or unfair treatment.
| Assessment | Rating |
|------------|--------|
| Likelihood | ☐ Negligible ☐ Low ☐ Medium ☐ High |
| Severity (if occurs) | ☐ Negligible ☐ Low ☐ Medium ☐ High |
| **Overall Risk** | ☐ Low ☐ Medium ☐ High ☐ Very High |
**Risk 3: Lack of Transparency**
**Description**: Data subjects may not understand how their data is processed or decisions are made.
| Assessment | Rating |
|------------|--------|
| Likelihood | ☐ Negligible ☐ Low ☐ Medium ☐ High |
| Severity (if occurs) | ☐ Negligible ☐ Low ☐ Medium ☐ High |
| **Overall Risk** | ☐ Low ☐ Medium ☐ High ☐ Very High |
**Risk 4: Inability to Exercise Data Subject Rights**
**Description**: Data subjects may have difficulty exercising their rights (access, erasure, portability, etc.).
| Assessment | Rating |
|------------|--------|
| Likelihood | ☐ Negligible ☐ Low ☐ Medium ☐ High |
| Severity (if occurs) | ☐ Negligible ☐ Low ☐ Medium ☐ High |
| **Overall Risk** | ☐ Low ☐ Medium ☐ High ☐ Very High |
**Risk 5: Data Quality Issues**
**Description**: Inaccurate or outdated data could lead to incorrect decisions or outcomes.
| Assessment | Rating |
|------------|--------|
| Likelihood | ☐ Negligible ☐ Low ☐ Medium ☐ High |
| Severity (if occurs) | ☐ Negligible ☐ Low ☐ Medium ☐ High |
| **Overall Risk** | ☐ Low ☐ Medium ☐ High ☐ Very High |
**Risk 6: Function Creep / Scope Expansion**
**Description**: Data collected for one purpose could be used for other purposes without consent.
| Assessment | Rating |
|------------|--------|
| Likelihood | ☐ Negligible ☐ Low ☐ Medium ☐ High |
| Severity (if occurs) | ☐ Negligible ☐ Low ☐ Medium ☐ High |
| **Overall Risk** | ☐ Low ☐ Medium ☐ High ☐ Very High |
**Additional Risks:**
[Add any processing-specific risks]
---
## 6. Mitigation Measures
For each identified risk, document mitigation measures:
### Risk 1: Unauthorized Access or Data Breach
**Mitigation Measures:**
- [ ] Encryption in transit (TLS 1.2+)
- [ ] Encryption at rest
- [ ] Multi-factor authentication
- [ ] Access controls (RBAC)
- [ ] Regular security audits
- [ ] WAF and DDoS protection (Bunny.net Shield)
- [ ] Multi-tenant data isolation
- [ ] Regular security training
- [ ] Incident response plan
- [ ] Other: _______________
**Residual Risk After Mitigation:** ☐ Low ☐ Medium ☐ High ☐ Very High
### Risk 2: Discrimination or Bias in Automated Decisions
**Mitigation Measures:**
- [ ] Regular bias testing of AI models
- [ ] Diverse training data sets
- [ ] Human review of automated decisions
- [ ] Clear criteria for decision-making
- [ ] Right to contest decisions
- [ ] Transparency about decision logic
- [ ] Regular fairness audits
- [ ] Monitoring of outcomes by demographic groups
- [ ] Ability to request explanation
- [ ] Other: _______________
**Residual Risk After Mitigation:** ☐ Low ☐ Medium ☐ High ☐ Very High
### Risk 3: Lack of Transparency
**Mitigation Measures:**
- [ ] Clear Privacy Policy explaining processing
- [ ] Explicit consent mechanisms
- [ ] Plain language explanations
- [ ] Information provided before data collection
- [ ] Explanation of automated decision logic
- [ ] Contact information for questions
- [ ] Regular communication with data subjects
- [ ] Privacy-by-design approach (anonymous until consent)
- [ ] Other: _______________
**Residual Risk After Mitigation:** ☐ Low ☐ Medium ☐ High ☐ Very High
### Risk 4: Inability to Exercise Data Subject Rights
**Mitigation Measures:**
- [ ] Clear procedures for rights requests
- [ ] Multiple request channels (email, helpdesk)
- [ ] 30-day response timeframe
- [ ] Technical capability to extract data
- [ ] Data portability in standard formats
- [ ] Secure deletion processes
- [ ] Account disabling/restriction capability
- [ ] Identity verification procedures
- [ ] Other: _______________
**Residual Risk After Mitigation:** ☐ Low ☐ Medium ☐ High ☐ Very High
### Risk 5: Data Quality Issues
**Mitigation Measures:**
- [ ] Data validation on input
- [ ] Regular data accuracy reviews
- [ ] Ability for data subjects to correct errors
- [ ] Clear data update procedures
- [ ] Data quality monitoring
- [ ] Source verification for third-party data
- [ ] Archiving of outdated data
- [ ] Other: _______________
**Residual Risk After Mitigation:** ☐ Low ☐ Medium ☐ High ☐ Very High
### Risk 6: Function Creep / Scope Expansion
**Mitigation Measures:**
- [ ] Documented purpose limitation
- [ ] Access controls preventing unauthorized use
- [ ] Regular compliance audits
- [ ] Privacy Policy clearly states purposes
- [ ] Consent required for new purposes
- [ ] Technical controls preventing misuse
- [ ] Staff training on data protection
- [ ] Other: _______________
**Residual Risk After Mitigation:** ☐ Low ☐ Medium ☐ High ☐ Very High
### Additional Mitigation Measures
[Document any additional mitigation measures not covered above]
---
## 7. Data Subject Rights Implementation
**How will you ensure data subjects can exercise their rights?**
### Right of Access (Article 15)
- [ ] Procedure documented
- [ ] Technical capability implemented
- [ ] Response within 30 days
- Method: _______________
### Right to Rectification (Article 16)
- [ ] Procedure documented
- [ ] Technical capability implemented
- [ ] Response within 30 days
- Method: _______________
### Right to Erasure (Article 17)
- [ ] Procedure documented
- [ ] Technical capability implemented
- [ ] Response within 30 days
- Method: _______________
- Limitations: _______________
### Right to Restriction (Article 18)
- [ ] Procedure documented
- [ ] Technical capability implemented (account disabling)
- [ ] Response within 30 days
### Right to Data Portability (Article 20)
- [ ] Procedure documented
- [ ] Technical capability implemented
- [ ] Export format: JSON / CSV / XML / Other: _______________
### Right to Object (Article 21)
- [ ] Procedure documented
- [ ] Opt-out mechanisms implemented
- [ ] Clear in Privacy Policy
### Rights Related to Automated Decision-Making (Article 22)
- [ ] Human intervention available
- [ ] Explanation of logic provided
- [ ] Right to contest implemented
- [ ] Documented in Privacy Policy
---
## 8. Privacy by Design and Default
**Privacy Enhancing Technologies Implemented:**
- [ ] Data minimization (collect only necessary data)
- [ ] Pseudonymization (where applicable)
- [ ] Anonymization (where applicable)
- [ ] Anonymous interaction until consent (privacy-by-design)
- [ ] Encryption (in transit and at rest)
- [ ] Access controls and authentication
- [ ] Audit logging
- [ ] Secure deletion
- [ ] Data isolation (multi-tenant architecture)
- [ ] Other: _______________
**Default Settings:**
- [ ] Most privacy-protective settings by default
- [ ] Opt-in (not opt-out) for non-essential processing
- [ ] Clear consent mechanisms before data collection
- [ ] Limited data sharing by default
---
## 9. Compliance with Principles
**For each GDPR principle, confirm compliance:**
### Lawfulness, Fairness, Transparency (Article 5(1)(a))
- [ ] Lawful basis identified and documented
- [ ] Processing is fair and transparent
- [ ] Privacy Policy clearly explains processing
- Evidence: _______________
### Purpose Limitation (Article 5(1)(b))
- [ ] Specific purposes documented
- [ ] Data not used for incompatible purposes
- [ ] New purposes require new consent/legal basis
- Evidence: _______________
### Data Minimization (Article 5(1)(c))
- [ ] Only necessary data collected
- [ ] Regular review of data collected
- [ ] Excess data not retained
- Evidence: _______________
### Accuracy (Article 5(1)(d))
- [ ] Mechanisms to ensure data accuracy
- [ ] Ability to correct inaccurate data
- [ ] Regular data quality reviews
- Evidence: _______________
### Storage Limitation (Article 5(1)(e))
- [ ] Retention periods defined and documented
- [ ] Automated deletion where appropriate
- [ ] Justification for retention documented
- Evidence: _______________
### Integrity and Confidentiality (Article 5(1)(f))
- [ ] Appropriate security measures implemented
- [ ] Protection against unauthorized access
- [ ] Encryption and access controls in place
- Evidence: See Annex 2 of DPA
### Accountability (Article 5(2))
- [ ] Documentation of compliance measures
- [ ] Records of processing activities maintained
- [ ] DPIA conducted and documented
- [ ] DPA in place with processors
- Evidence: This DPIA, DPA with customers
---
## 10. International Transfers
**Does this processing involve transfer to third countries?**
☐ No - all processing within EU
☐ Yes (complete below)
**If yes:**
**Country/Region:** _______________
**Transfer Mechanism:**
- [ ] Adequacy decision (Article 45)
- [ ] Standard Contractual Clauses (Article 46)
- [ ] Binding Corporate Rules (Article 47)
- [ ] Other: _______________
**Transfer Impact Assessment Completed?** ☐ Yes ☐ No
**Additional Safeguards:**
[Document supplementary measures to ensure adequate protection]
---
## 11. Documentation and Records
**Documentation Maintained:**
- [ ] This DPIA
- [ ] Privacy Policy
- [ ] Data Processing Agreement
- [ ] Consent records (if applicable)
- [ ] Records of processing activities (Article 30)
- [ ] Data breach register
- [ ] Data Subject rights request log
- [ ] Staff training records
- [ ] Sub-processor agreements
**Record of Processing Activities (Article 30) Completed?**
☐ Yes ☐ No ☐ In Progress
---
## 12. Outcomes and Recommendations
### 12.1 Overall Risk Assessment
**After implementing mitigation measures, what is the residual risk level?**
☐ Low - processing can proceed
☐ Medium - additional measures recommended
☐ High - significant concerns, consult DPO/legal counsel
☐ Very High - processing should not proceed without major changes
### 12.2 Recommendations
**Recommended Actions Before Processing Begins:**
1. [Action item 1]
2. [Action item 2]
3. [Action item 3]
**Recommended Monitoring/Review Activities:**
1. [Monitoring item 1]
2. [Monitoring item 2]
3. [Monitoring item 3]
### 12.3 Consultation with Supervisory Authority
**Is consultation with supervisory authority required?**
☐ No - residual risk is acceptable
☐ Yes - high residual risk remains despite mitigation (Article 36)
**If yes, when will consultation occur?** _______________
### 12.4 Sign-Off
**DPIA Completed By:**
Name: _______________
Role: _______________
Date: _______________
Signature: _______________
**Reviewed and Approved By:**
Name: _______________
Role: _______________
Date: _______________
Signature: _______________
**Next Review Date:** _______________
*(Recommend annual review or when significant changes occur)*
---
## Appendix A: Completed Example - Job Candidate Assessment
This appendix provides a completed example for reference.
### Example: Job Candidate Assessment Specialist
**Processing Activity**: AI-powered job candidate assessment tool
**Personal Data Processed**:
- Assessment responses (text)
- Communication records (chatbot interactions)
- Contact information (name, email) - collected AFTER assessment with consent
- Assessment scores/results
**Purpose**: To assess candidates' suitability for job roles based on their responses to standardized questions
**Lawful Basis**:
- Consent (candidates explicitly consent before providing contact information)
- Contract (processing necessary to take steps at request of data subject prior to entering into contract)
**Automated Decision-Making**: Yes, with human oversight. Candidates are assessed by AI, but:
- Contact information only collected AFTER positive assessment
- Human recruiter makes final hiring decisions
- Candidates can restart assessment at any time
- Candidates informed about AI assessment before beginning
**Key Risks Identified**:
1. Bias/discrimination in assessment algorithms - MEDIUM risk
2. Lack of transparency about assessment criteria - MEDIUM risk
3. Data breach exposing candidate information - LOW risk (after mitigation)
**Key Mitigation Measures**:
- Anonymous assessment until consent obtained
- Clear explanation of assessment process
- Right to contest results
- Human review of all final decisions
- Regular bias testing of algorithms
- Strong technical security measures (encryption, access controls)
- 12-month retention period with secure deletion
**Residual Risk**: LOW - processing can proceed
**Special Considerations**:
- Candidates must be informed about automated decision-making
- Privacy Policy must explain assessment logic
- Contact information collected only after explicit consent
- Right to human intervention clearly communicated
---
## Appendix B: Resources and References
**GDPR Articles Referenced:**
- Article 5: Principles relating to processing
- Article 6: Lawfulness of processing
- Article 9: Special categories of data
- Article 13-14: Information to be provided
- Article 15-22: Data subject rights
- Article 22: Automated decision-making
- Article 28: Processor obligations
- Article 30: Records of processing activities
- Article 33-34: Data breach notification
- Article 35: Data Protection Impact Assessment
- Article 36: Prior consultation with supervisory authority
- Article 45-46: International transfers
**Additional Guidance:**
- WP29 Guidelines on DPIAs (WP 248)
- WP29 Guidelines on Automated Decision-Making (WP 251)
- ICO DPIA Guidance
- EDPB Guidelines on processing personal data for scientific research
- Belgian DPA Guidance (https://www.gegevensbeschermingsautoriteit.be)
**Internal Documents:**
- Ask Eve AI Data Protection Agreement
- Ask Eve AI Privacy Policy
- Technical and Organizational Measures (DPA Annex 2)
---
**End of DPIA Template**

View File

@@ -4,17 +4,17 @@ No, we do not currently have a formal information security policy document. As a
However, we do maintain several key security practices: However, we do maintain several key security practices:
Product Security (Evie SaaS Platform): Product Security (Evie SaaS Platform):
Multi-tenant architecture with strict data isolation (separate database schemas and object storage folders per tenant) - Multi-tenant architecture with strict data isolation (separate database schemas and object storage folders per tenant)
Hosted exclusively with European providers (Scaleway, Bunny.net, Mistral) compliant with EU regulations - Hosted exclusively with European providers (Scaleway, Bunny.net, Mistral) compliant with EU regulations
Published Privacy Policy and Terms & Conditions - Published Privacy Policy and Terms & Conditions
GDPR-compliant data handling practices - GDPR-compliant data handling practices
Internal Operations Security: Internal Operations Security:
Secure password and credential management using Proton Pass - Secure password and credential management using Proton Pass
All internal business data maintained in secure cloud environments (Proton, Dropbox, Canva) - All internal business data maintained in secure cloud environments (Proton, Dropbox, Canva)
Code versioning and backup through GitHub - Code versioning and backup through GitHub
Controlled access to all systems and services - Controlled access to all systems and services
We plan to formalise our security practices into a comprehensive security policy as our organisation scales beyond 10 employees. We plan to formalise our security practices into a comprehensive security policy as our organisation scales beyond 10 employees.

View File

@@ -124,13 +124,13 @@ Ask Eve AI System Data is the data required to enable Ask Eve AI to:
The following personal information is gathered: The following personal information is gathered:
1. *Account / User Information*: This information enables a user to log 1. *Account / User Information*: This information enables a user to log
into the Ask Eve AI systems, or to subscribe to the system's into the Ask Eve AI systems, or to subscribe to the system's
services. It includes name, e-mail address, a secured password and services. It includes name, e-mail address, a secured password and
roles in the system. roles in the system.
2. *Tenant / Customer Information*: Although not personal data in the 2. *Tenant / Customer Information*: Although not personal data in the
strict sense, in order to subscribe to the services provided by Ask strict sense, in order to subscribe to the services provided by Ask
Eve AI, payment information such as financial details, VAT numbers, Eve AI, payment information such as financial details, VAT numbers,
valid addresses and email information is required. valid addresses and email information is required.
**Tenant Data:** **Tenant Data:**
@@ -151,15 +151,15 @@ There's no personal data collected explicitly, however, the following
personal information is gathered: personal information is gathered:
1. *End User Content*: Ask Eve AI collects Personal Data that the End 1. *End User Content*: Ask Eve AI collects Personal Data that the End
User provides in the input to our Services ("Content") as is. User provides in the input to our Services ("Content") as is.
2. *Communication Information*: If the Customer communicates with Ask 2. *Communication Information*: If the Customer communicates with Ask
Eve AI, such as via email, our pages on social media sites or the Eve AI, such as via email, our pages on social media sites or the
chatbots or other interfaces we provide to our services, Ask Eve AI chatbots or other interfaces we provide to our services, Ask Eve AI
may collect Personal Data like name, contact information, and the may collect Personal Data like name, contact information, and the
contents of the messages the Customer sends ("Communication contents of the messages the Customer sends ("Communication
Information"). End User personal information may be provided by End Information"). End User personal information may be provided by End
User in interactions with Ask Eve AI's services, and as such will be User in interactions with Ask Eve AI's services, and as such will be
stored in Ask Eve AI's services as is. stored in Ask Eve AI's services as is.
> >
@@ -170,52 +170,46 @@ personal information is gathered:
> contact or provide us with information to establish your identity or > contact or provide us with information to establish your identity or
> age. > age.
> **Technical Data:**\\
>
> \
**Technical Data:**\
When you visit, use, or interact with the Services, we receive the When you visit, use, or interact with the Services, we receive the
following information about your visit, use, or interactions ("Technical following information about your visit, use, or interactions ("Technical
Information"): Information"):
1. *Log Data:* Ask Eve AI collects information that your browser or 1. *Log Data:* Ask Eve AI collects information that your browser or
device automatically sends when the Customer uses the Services. Log device automatically sends when the Customer uses the Services. Log
data includes the Internet Protocol address, browser type and data includes the Internet Protocol address, browser type and
settings, the date and time of your request, and how the Customer settings, the date and time of your request, and how the Customer
interacts with the Services. interacts with the Services.
2. *Usage Data:* Ask Eve AI collects information about the use of the 2. *Usage Data:* Ask Eve AI collects information about the use of the
Services, such as the types of content that the Customer views or Services, such as the types of content that the Customer views or
engages with, the features the Customer uses and the actions the engages with, the features the Customer uses and the actions the
Customer takes, as well as the Customer's time zone, country, the Customer takes, as well as the Customer's time zone, country, the
dates and times of access, user agent and version, type of computer dates and times of access, user agent and version, type of computer
or mobile device, and the Customer's computer connection. or mobile device, and the Customer's computer connection.
3. *Interaction Data*: Ask Eve AI collects the data you provide when 3. *Interaction Data*: Ask Eve AI collects the data you provide when
interacting with it's services, such as interacting with a chatbot interacting with it's services, such as interacting with a chatbot
or similar advanced means. or similar advanced means.
4. *Device Information:* Ask Eve AI collects information about the 4. *Device Information:* Ask Eve AI collects information about the
device the Customer uses to access the Services, such as the name of device the Customer uses to access the Services, such as the name of
the device, operating system, device identifiers, and browser you the device, operating system, device identifiers, and browser you
are using. Information collected may depend on the type of device are using. Information collected may depend on the type of device
the Customer uses and its settings. the Customer uses and its settings.
5. *Location Information:* Ask Eve AI may determine the general area 5. *Location Information:* Ask Eve AI may determine the general area
from which your device accesses our Services based on information from which your device accesses our Services based on information
like its IP address for security reasons and to make your product like its IP address for security reasons and to make your product
experience better, for example to protect the Customer's account by experience better, for example to protect the Customer's account by
detecting unusual login activity or to provide more accurate detecting unusual login activity or to provide more accurate
responses. In addition, some of our Services allow the Customer to responses. In addition, some of our Services allow the Customer to
choose to provide more precise location information from the choose to provide more precise location information from the
Customer's device, such as location information from your device's Customer's device, such as location information from your device's
GPS. GPS.
6. *Cookies and Similar Technologies:* Ask Eve AI uses cookies and 6. *Cookies and Similar Technologies:* Ask Eve AI uses cookies and
similar technologies to operate and administer our Services, and similar technologies to operate and administer our Services, and
improve your experience. If the Customer uses the Services without improve your experience. If the Customer uses the Services without
creating an account, Ask Eve AI may store some of the information creating an account, Ask Eve AI may store some of the information
described in this Agreement with cookies, for example to help described in this Agreement with cookies, for example to help
maintain the Customer's preferences across browsing sessions. For maintain the Customer's preferences across browsing sessions. For
details about our use of cookies, please read our Cookie Policy. details about our use of cookies, please read our Cookie Policy.
**External Data:** **External Data:**
@@ -253,11 +247,11 @@ and not attempt to reidentify the information, unless required by law.
As noted above, Ask Eve AI may use content the Customer provides Ask Eve As noted above, Ask Eve AI may use content the Customer provides Ask Eve
AI to improve the Services, for example to train the models that power AI to improve the Services, for example to train the models that power
Ask Eve AI. Read [**our instructions**(opens in a new Ask Eve AI. Read [\**our instructions*\*(opens in a new
window)**](https://help.openai.com/en/articles/5722486-how-your-data-is-used-to-improve-model-performance) on window)\*\*](https://help.openai.com/en/articles/5722486-how-your-data-is-used-to-improve-model-performance) on
how you can opt out of our use of your Content to train our models.\ how you can opt out of our use of your Content to train our models.\\
1. 1. ## Instructions {#instructions-3} 1. 1. \#\# Instructions {#instructions-3}
Data Processor shall only Process Personal Data of Data Controller on Data Processor shall only Process Personal Data of Data Controller on
behalf of the Data Controller and in accordance with this Data behalf of the Data Controller and in accordance with this Data
@@ -267,12 +261,12 @@ manner, as is reasonably necessary to provide the Services in accordance
with the Agreement. Data Controller shall only give instructions that with the Agreement. Data Controller shall only give instructions that
comply with the Data Protection legislation. comply with the Data Protection legislation.
2. 1. ## Applicable mandatory laws {#applicable-mandatory-laws-3} 2. 1. \#\# Applicable mandatory laws {#applicable-mandatory-laws-3}
Data Processor shall only Process as required by applicable mandatory Data Processor shall only Process as required by applicable mandatory
laws and always in compliance with Data Protection Legislation.\ laws and always in compliance with Data Protection Legislation.\\
3. 1. ## Transfer to a third party {#transfer-to-a-third-party-3} 3. 1. \#\# Transfer to a third party {#transfer-to-a-third-party-3}
Data Processor uses functionality of third party services to realise Data Processor uses functionality of third party services to realise
it's functionality. For the purpose of realising Ask Eve AI's it's functionality. For the purpose of realising Ask Eve AI's
@@ -284,7 +278,7 @@ other third party and/or appoint any third party as a sub-processor of
Personal Data unless it is legally required or in case of a notification Personal Data unless it is legally required or in case of a notification
to the Data Controller by which he gives his consent. to the Data Controller by which he gives his consent.
4. 1. ## Transfer to a Third Country {#transfer-to-a-third-country-3} 4. 1. \#\# Transfer to a Third Country {#transfer-to-a-third-country-3}
Data Processor shall not transfer Personal Data (including any transfer Data Processor shall not transfer Personal Data (including any transfer
via electronic media) to any Third Country without the prior written via electronic media) to any Third Country without the prior written
@@ -305,9 +299,9 @@ Data Controller about the particular measures taken to guarantee the
protection of the Personal Data of the Data Subject in accordance with protection of the Personal Data of the Data Subject in accordance with
the Regulation. the Regulation.
\ \\
5. 1. ## Data secrecy {#data-secrecy-3} 5. 1. \#\# Data secrecy {#data-secrecy-3}
The Data Processor shall maintain data secrecy in accordance with The Data Processor shall maintain data secrecy in accordance with
applicable Data Protection Legislation and shall take all reasonable applicable Data Protection Legislation and shall take all reasonable
@@ -324,7 +318,7 @@ steps to ensure that:
> in accordance with applicable Data Protection Legislation and at all > in accordance with applicable Data Protection Legislation and at all
> times act in compliance with the Data Protection Obligations. > times act in compliance with the Data Protection Obligations.
6. 1. ## Appropriate technical and organizational measures {#appropriate-technical-and-organizational-measures-3} 6. 1. \#\# Appropriate technical and organizational measures {#appropriate-technical-and-organizational-measures-3}
Data Processor has implemented (and shall comply with) all appropriate Data Processor has implemented (and shall comply with) all appropriate
technical and organizational measures to ensure the security of the technical and organizational measures to ensure the security of the
@@ -348,7 +342,7 @@ registration, de-registration and withdrawal of automation access codes
(API Keys), and is also responsible for the complete physical security (API Keys), and is also responsible for the complete physical security
of its environment. of its environment.
7. 1. ## Assistance and co-operation {#assistance-and-co-operation-3} 7. 1. \#\# Assistance and co-operation {#assistance-and-co-operation-3}
The Data Processor shall provide the Data Controller with such The Data Processor shall provide the Data Controller with such
assistance and co-operation as the Data Controller may reasonably assistance and co-operation as the Data Controller may reasonably
@@ -358,7 +352,7 @@ Data processed by the Data Processor, including but not limited to:
> \(1\) on request of the Data Controller, promptly providing written > \(1\) on request of the Data Controller, promptly providing written
> information regarding the technical and organizational measures which > information regarding the technical and organizational measures which
> the Data Processor has implemented to safeguard Personal Data;\ > the Data Processor has implemented to safeguard Personal Data;\\
> \(2\) disclosing full and relevant details in respect of any and all > \(2\) disclosing full and relevant details in respect of any and all
> government, law enforcement or other access protocols or controls > government, law enforcement or other access protocols or controls
@@ -401,7 +395,7 @@ Data processed by the Data Processor, including but not limited to:
> Processor shall support the Data Controller in the provision of such > Processor shall support the Data Controller in the provision of such
> information when explicitly requested by the Data Controller. > information when explicitly requested by the Data Controller.
4. # Audit {#audit-1} 4. \# Audit {#audit-1}
At the Data Controller's request the Data Processor shall provide the At the Data Controller's request the Data Processor shall provide the
Data Controller with all information needed to demonstrate that it Data Controller with all information needed to demonstrate that it
@@ -423,7 +417,7 @@ minimum, and the Data Controller shall impose sufficient confidentiality
obligations on its auditors. Every auditor who does an inspection will obligations on its auditors. Every auditor who does an inspection will
be at all times accompanied by a dedicated employee of the Processor. be at all times accompanied by a dedicated employee of the Processor.
4. # Liability {#liability-1} 4. \# Liability {#liability-1}
Each Party shall be liable for any suffered foreseeable, direct and Each Party shall be liable for any suffered foreseeable, direct and
personal damages ("Direct Damages") resulting from any attributable personal damages ("Direct Damages") resulting from any attributable
@@ -458,7 +452,7 @@ immediately prior to the cause of damages. In no event shall the Data
Processor be held liable if the Data Processor can prove he is not Processor be held liable if the Data Processor can prove he is not
responsible for the event or cause giving rise to the damage. responsible for the event or cause giving rise to the damage.
4. # Term {#term-1} 4. \# Term {#term-1}
This Data Processing Agreement shall be valid for as long as the This Data Processing Agreement shall be valid for as long as the
Customer uses the Services. Customer uses the Services.
@@ -469,7 +463,7 @@ use of Personal Data and delete all Personal Data and copies thereof in
its possession unless otherwise agreed or when deletion of the Personal its possession unless otherwise agreed or when deletion of the Personal
Data should be technically impossible. Data should be technically impossible.
4. # Governing law -- jurisdiction {#governing-law-jurisdiction-1} 4. \# Governing law -- jurisdiction {#governing-law-jurisdiction-1}
This Data Processing Agreement and any non-contractual obligations This Data Processing Agreement and any non-contractual obligations
arising out of or in connection with it shall be governed by and arising out of or in connection with it shall be governed by and
@@ -482,91 +476,18 @@ litigation concerning or related to this Data Processing Agreement,
without any exception, shall be submitted to the exclusive jurisdiction without any exception, shall be submitted to the exclusive jurisdiction
of the courts of Gent, Belgium. of the courts of Gent, Belgium.
# Annex1 # Annex1
# Sub-Processors # Sub-Processors
The Data Controller hereby agrees to the following list of The Data Controller hereby agrees to the following list of
Sub-Processors, engaged by the Data Processor for the Processing of Sub-Processors, engaged by the Data Processor for the Processing of
Personal Data under the Agreement: Personal Data under the Agreement:
+-------------+--------------------------------------------------------+
| | |
+=============+========================================================+
| **Open AI** | |
+-------------+--------------------------------------------------------+
| Address | OpenAI, L.L.C., |
| | |
| | 3180 18th St, San Francisco, |
| | |
| | CA 94110, |
| | |
| | United States of America. |
+-------------+--------------------------------------------------------+
| Contact | OpenAI's Data Protection team |
| | |
| | dsar@openai.com |
+-------------+--------------------------------------------------------+
| Description | Ask Eve AI accesses Open AI's models through Open AI's |
| | API to realise it's functionality. |
| | |
| | Services are GDPR compliant. |
+-------------+--------------------------------------------------------+
| | |
+-------------+--------------------------------------------------------+
+---------------+------------------------------------------------------+ # Annex 2
| | |
+===============+======================================================+
| **StackHero** | |
+---------------+------------------------------------------------------+
| Address | Stackhero |
| | |
| | 1 rue de Stockholm |
| | |
| | 75008 Paris |
| | |
| | France |
+---------------+------------------------------------------------------+
| Contact | support@stackhero.io |
+---------------+------------------------------------------------------+
| Description | StackHero is Ask Eve AI's cloud provider, and hosts |
| | the services for PostgreSQL, Redis, Docker, Minio |
| | and Greylog. |
| | |
| | Services are GDPR compliant. |
+---------------+------------------------------------------------------+
| **** | |
+---------------+------------------------------------------------------+
+----------------+-----------------------------------------------------+ # []{#anchor-7}Technical and organizational measures
| | |
+================+=====================================================+
| **A2 Hosting** | |
+----------------+-----------------------------------------------------+
| Address | A2 Hosting, Inc. |
| | |
| | PO Box 2998 |
| | |
| | Ann Arbor, MI 48106 |
| | |
| | United States |
+----------------+-----------------------------------------------------+
| Contact | [*+1 734-222-4678*](tel:+1(734)222-4678) |
+----------------+-----------------------------------------------------+
| Description | A2 hosting is hosting our main webserver and |
| | mailserver. They are all hosted on European servers |
| | (Iceland). It does not handle data of our business |
| | applications. |
| | |
| | Services are GDPR compliant. |
+----------------+-----------------------------------------------------+
| **** | |
+----------------+-----------------------------------------------------+
# Annex 2
# []{#anchor-7}Technical and organizational measures
# 1. Purpose of this document # 1. Purpose of this document
@@ -580,13 +501,13 @@ list below following a Data Protection Impact Assessment (DPIA).
These measures are designed to: These measures are designed to:
1. ensure the security and confidentiality of Ask Eve AI managed data, 1. ensure the security and confidentiality of Ask Eve AI managed data,
information, applications and infrastructure; information, applications and infrastructure;
2. protect against any anticipated threats or hazards to the security 2. protect against any anticipated threats or hazards to the security
and integrity of Personal Data, Ask Eve AI Intellectual Property, and integrity of Personal Data, Ask Eve AI Intellectual Property,
Infrastructure or other business-critical assets; Infrastructure or other business-critical assets;
3. protect against any actual unauthorized processing, loss, use, 3. protect against any actual unauthorized processing, loss, use,
disclosure or acquisition of or access to any Personal Data or other disclosure or acquisition of or access to any Personal Data or other
business-critical information or data managed by Ask Eve AI. business-critical information or data managed by Ask Eve AI.
Ask Eve AI ensures that all its Sub-Processors have provided the Ask Eve AI ensures that all its Sub-Processors have provided the
necessary and required guarantees on the protection of personal data necessary and required guarantees on the protection of personal data
@@ -614,7 +535,7 @@ infrastructure. Ask Eve AI uses an intent-based approach where
activities are constantly monitored, analysed and benchmarked instead of activities are constantly monitored, analysed and benchmarked instead of
relying solely on a simple authentication/authorization trust model. relying solely on a simple authentication/authorization trust model.
4. 1. ## General Governance & Awareness {#general-governance-awareness-3} 4. 1. \#\# General Governance & Awareness {#general-governance-awareness-3}
As a product company, Ask Eve AI is committed to maintain and preserve As a product company, Ask Eve AI is committed to maintain and preserve
an IT infrastructure that has a robust security architecture, complies an IT infrastructure that has a robust security architecture, complies
@@ -676,7 +597,7 @@ enabled.
Key management governance is implemented and handled by Facilities. Key management governance is implemented and handled by Facilities.
1. 1. ## Endpoint Security & User Accounts {#endpoint-security-user-accounts-3} 1. 1. \#\# Endpoint Security & User Accounts {#endpoint-security-user-accounts-3}
All endpoints and any information stored are encrypted using All endpoints and any information stored are encrypted using
enterprise-grade encryption on all operating systems supported by Ask enterprise-grade encryption on all operating systems supported by Ask
@@ -701,7 +622,7 @@ ensure endpoint integrity and policy compliance.
Access is managed according to role-based access control principles and Access is managed according to role-based access control principles and
all user behavior on Ask Eve AI platforms is audited. all user behavior on Ask Eve AI platforms is audited.
1. 1. ## Data Storage, Recovery & Securing Personal Data {#data-storage-recovery-securing-personal-data-3} 1. 1. \#\# Data Storage, Recovery & Securing Personal Data {#data-storage-recovery-securing-personal-data-3}
> Ask Eve AI has deployed: > Ask Eve AI has deployed:
@@ -720,7 +641,7 @@ all user behavior on Ask Eve AI platforms is audited.
- Records of the processing activities. - Records of the processing activities.
- Data Retention Policies - Data Retention Policies
1. 1. ## Protection & Insurance {#protection-insurance-3} 1. 1. \#\# Protection & Insurance {#protection-insurance-3}
Ask Eve AI has a cyber-crime insurance policy. Details on the policy can Ask Eve AI has a cyber-crime insurance policy. Details on the policy can
be requested through the legal department. be requested through the legal department.

1143
content/dpa/1.1/1.1.0.md Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -18,7 +18,7 @@ To access certain features of the Service, you must register for an account. You
### 4. Privacy ### 4. Privacy
Your use of the Service is also governed by our Privacy Policy, which can be found [here](/content/privacy). Your use of the Service is also governed by our Privacy Policy, which can be found [here](/content/dpa).
### 5. Intellectual Property ### 5. Intellectual Property

454
content/terms/1.1/1.1.0.md Normal file
View File

@@ -0,0 +1,454 @@
# Terms of Service
## Ask Eve AI
**Version 1.0.0**
**Effective Date: October 3, 2025**
---
## Introduction
These Terms of Service ("Terms") constitute a legally binding agreement between **Flow IT BV**, with registered office at Toekomststraat 62, 9800 Deinze, Belgium, with company number BE0877.273.542, operating under the trademark **Ask Eve AI** ("Ask Eve AI," "AskEveAI," "we," "us," or "our"), and the Customer (as defined below) that governs the use of the Services (as defined below).
By signing up to use the Services, accessing the Services, or clicking to accept these Terms, you ("Customer," "you," or "your") agree to be bound by these Terms. You represent that you are lawfully able to enter into contracts and, if you are entering into these Terms on behalf of an entity, that you have legal authority to bind that entity.
**For commercial customers**: Your use of the Services is also subject to our [Data Protection Agreement](link-to-dpa), which governs the processing of personal data. In the event of any conflict between these Terms and the Data Protection Agreement regarding data protection matters, the Data Protection Agreement shall prevail.
---
## 1. Services
### 1.1 Provision of Services
1. Upon payment of the applicable fees, Ask Eve AI grants to Customer a non-exclusive, non-transferable, non-sublicensable right to access and use the Ask Eve AI platform ("Platform" or "Services") during the term as stated in these Terms and as specified in the applicable subscription for Customer's business operations.
2. Ask Eve AI may subcontract to third parties any part of the Services. In particular, Ask Eve AI utilizes third-party service providers to provide, amongst others, connectivity, AI services (including large language models), data centre services, database services, content delivery, and security services. A complete list of Sub-Processors is available in Annex 1 of our Data Protection Agreement.
3. Customer must provide accurate and up-to-date account information. Customer is responsible for all activities that occur under its account, including the activities of any authorized user or Partner. Customer shall:
- Notify Ask Eve AI immediately of any unauthorized use of any password, API key, or user ID, or any other known or suspected breach of security
- Use reasonable efforts to stop any unauthorized use of the Services that is known or suspected by Customer
- Not provide false identity information to gain access to or use the Services
- Maintain proper access controls for all users and API credentials
### 1.2 Limitations on Use of Services
1. **Prohibited Actions**: Customer shall not:
- Remove any identification, proprietary, copyright, or other notices in the Services or documentation
- Represent that output was human-generated when it was not
- Reverse engineer the Services into source code, decompile, disassemble, or analyze the Services by "reverse engineering"
- Create derivative works of the Services
- Merge the Services with other software
- Sublicense, sell, lease, or otherwise encumber rights granted by Ask Eve AI (unless expressly authorized by Ask Eve AI in writing)
- Use the Services in any way that causes, or may cause, damage to the Services or impairment of the availability or accessibility of the Services
- Use the Services in any way that is unlawful, illegal, fraudulent, or harmful, or in connection with any unlawful, illegal, fraudulent, or harmful purpose or activity
- Attempt to gain unauthorized access to any portion of the Services or related systems or networks
- Overload, flood, or perform denial-of-service attacks on the Services
- Use automated means to access the Services except through approved APIs and within documented rate limits
2. **Prohibited Content**: Customer shall not use the Services to create, upload, transmit, distribute, or store content that:
- Is illegal, including content depicting or facilitating child exploitation, terrorism, illegal drugs, or other criminal activity
- Contains malware, viruses, or malicious code
- Infringes intellectual property rights, including pirated material or unauthorized use of trademarks
- Constitutes spam, phishing attempts, or fraudulent schemes
- Includes personal data without proper consent or legal basis under applicable data protection laws
- Promotes hate speech, violence, or discrimination
- Attempts to manipulate AI systems to produce harmful, misleading, or unauthorized outputs
- Creates deepfakes or other misleading content intended to deceive
- Violates any applicable laws or regulations
3. **Enforcement**: In case of infringement of these limitations, Ask Eve AI reserves all rights to prove and obtain compensation for its full damages incurred by such infringement. This provision does not prevent Ask Eve AI from obtaining equitable relief in summary or other proceedings. Ask Eve AI may immediately suspend or terminate access to the Services upon discovery of any violation.
### 1.3 Acceptable Use and Compliance
1. **Data Protection Compliance**:
- **Customers** and **Partners** must comply with all applicable data protection laws, including the General Data Protection Regulation (GDPR) and the Belgian Data Protection Act, when using the Services.
- Customers and Partners are responsible for obtaining all necessary consents, authorizations, and legal bases required to process personal data through the Services.
- Customers and Partners must ensure their end users are properly informed about data processing activities and that appropriate privacy notices are provided.
- Although Ask Eve AI provides consent management functionality within the Platform, Customers and Partners remain solely responsible for ensuring their use of the Services complies with all applicable data protection requirements.
2. **Customer and Partner Indemnification for GDPR Violations**: Customer and Partner agree to indemnify, defend, and hold Ask Eve AI harmless from and against any claims, damages, losses, liabilities, costs, and expenses (including reasonable legal fees) arising from or related to Customer's or Partner's failure to comply with GDPR or other applicable data protection laws.
3. **Export Controls and Trade Compliance**: Customer certifies that it will comply with all applicable EU trade restrictions, export controls, and economic sanctions. Customer represents and warrants that it will not use the Services in any country or territory subject to EU or international sanctions, or in violation of any applicable trade restrictions.
---
## 2. Content
### 2.1 Input and Output
1. Customer may provide input to the Services ("Input") and receive output from the Services based on the Input ("Output"). Input and Output are collectively "Content."
2. Customer is responsible for all Content, including ensuring that it does not violate any applicable law or these Terms. Customer represents and warrants that it has all rights, licenses, and permissions needed to provide Input to the Services.
### 2.2 Ownership
1. **Customer Ownership**: Customer:
- Retains all ownership rights in Input
- Owns all Output generated by the Services based on Customer's Input
- Owns all specialist configurations, prompts, business logic, and custom implementations created by Customer on the Platform
2. **Ask Eve AI Assignment**: Ask Eve AI hereby assigns to Customer all of our right, title, and interest, if any, in and to Output generated specifically for Customer.
3. **Platform Ownership**: Ask Eve AI retains all ownership rights in and to the Platform itself, including all software, improvements, enhancements, modifications, AI models, core functionality, and intellectual property rights related thereto.
### 2.3 Non-Unique Outputs
Due to the nature of AI services and machine learning generally, Output may not be unique. Other users may receive similar output from the Services. Ask Eve AI's assignment of Output to Customer does not extend to other users' output or any third-party output.
### 2.4 Use of Content by Ask Eve AI
Ask Eve AI may use Content to:
- Provide, maintain, develop, and improve the Services
- Comply with applicable law
- Enforce our terms and policies
- Keep the Services safe and secure
- Generate aggregated or de-identified data for research, development, and model improvement, subject to the opt-out provisions in our Data Protection Agreement
### 2.5 Nature of AI and Customer Responsibilities
1. **AI Limitations**: Artificial intelligence and machine learning are rapidly evolving fields. Ask Eve AI is constantly working to improve the Services to make them more accurate, reliable, safe, and beneficial. However, given the probabilistic nature of machine learning, use of the Services may, in some situations, result in Output that does not accurately reflect real people, places, or facts.
2. **Customer Acknowledgments**: When Customer uses the Services, Customer understands and agrees that:
- **Output may not always be accurate**: Customer should not rely on Output from the Services as a sole source of truth or factual information, or as a substitute for professional advice
- **Human review required**: Customer must evaluate Output for accuracy and appropriateness for its use case, including using human review as appropriate, before using or sharing Output from the Services
- **No automated decisions affecting individuals**: Customer must not use any Output relating to a person for any purpose that could have a legal or material impact on that person, such as making credit, educational, employment, housing, insurance, legal, medical, or other important decisions about them, without appropriate human oversight and intervention
- **Potential for inappropriate content**: The Services may provide incomplete, incorrect, or offensive Output that does not represent Ask Eve AI's views
- **No endorsements**: If Output references any third-party products or services, it does not mean the third party endorses or is affiliated with Ask Eve AI
---
## 3. Intellectual Property
### 3.1 Ask Eve AI Ownership
Except as expressly set forth in these Terms, Ask Eve AI owns and retains all right, title, and interest in and to the Services, including:
- The Platform with all software, improvements, enhancements, or modifications thereto
- Any software, applications, inventions, or other technology developed as part of any maintenance or support
- All AI models, algorithms, and training methodologies
- All Intellectual Property Rights related to any of the foregoing
"Intellectual Property Rights" means current and future worldwide rights under patent, copyright, trade secret, trademark, moral rights, and other similar rights.
### 3.2 Reservation of Rights
All rights in and to Ask Eve AI not expressly granted to Customer in these Terms are reserved by Ask Eve AI. No license is granted to Customer except as to use of the Services as expressly stated herein. These Terms do not grant Customer:
- Any rights to the Intellectual Property Rights in the Platform or Services
- Any rights to use the Ask Eve AI trademarks, logos, domain names, or other brand features unless otherwise agreed in writing
### 3.3 Partner Implementations
Where Partners implement functionality on the Platform involving Ask Eve AI:
- Partners retain ownership of their specific implementations, configurations, and custom code
- Partners grant Ask Eve AI a license to host, operate, and provide their implementations as part of the Services
- Ask Eve AI retains ownership of the underlying Platform infrastructure and core functionality
- Partners are responsible for ensuring their implementations comply with these Terms and all applicable laws
---
## 4. Pricing and Payment
### 4.1 Subscription Model
1. **Paid Subscriptions**: Customer can only purchase a paid subscription ("Paid Subscription") by paying Basic Fees in advance on a monthly or yearly basis, or at another recurring interval agreed upon prior to purchase, through a third-party payment platform as indicated by Ask Eve AI.
2. **Third-Party Payment Terms**: Where payment is processed through a third-party payment platform, the separate terms and conditions of that payment platform shall apply in addition to these Terms.
### 4.2 Fee Structure
1. **Basic Fees**: Prepaid fees for the base subscription tier, covering specified usage limits for the billing period. Basic Fees must be paid in advance for each billing period to maintain access to the Services.
2. **Additional Fees**: Additional Fees will be charged to Customer on a monthly basis on top of the Basic Fees when the effective usage of the Services exceeds the usage limits covered by the Basic Fee for the respective month. Additional Fees will be calculated and invoiced to Customer through the same third-party payment platform.
3. **Overage Options**:
- Customer may enable or disable overage usage for each service element (storage, embeddings, interactions) as defined in the subscription agreement
- If overage is disabled and usage limits are reached, Services will be suspended until the next billing period or until Customer enables overage
- Customer may request changes to overage settings mid-period by contacting Ask Eve AI or their managing Partner
- Usage metrics are displayed in the administrative interface
### 4.3 Payment Terms
1. **Currency and Taxes**: All prices are quoted in EUR unless otherwise agreed. Tax rates are calculated based on the information Customer provides and the applicable rate at the time of payment. Prices do not include VAT, which will be added at the applicable rate.
2. **Billing Cycle**: Unless otherwise specified between the Parties, Paid Subscriptions will continue indefinitely until cancelled. Customer will receive a recurring invoice on the first day of each billing period for Basic Fees and will authorize the applicable third-party payment platform to charge the payment method for the then-current subscription fee.
3. **Payment Deadline**: Payment of each invoiced amount for Additional Fees, taxes included, must be completed within thirty (30) days after the date of the invoice.
4. **Late Payment**: Any payment after the fixed payment date shall be subject to delay interest for late payment in accordance with the Law of 2 August 2002 on combating late payment in commercial transactions, calculated at the legal interest rate as determined by the Belgian government. This provision shall not in any event exclude the possible payment of damages.
5. **Invoice Complaints**: Complaints relating to invoices must be notified to Ask Eve AI directly and in writing within fifteen (15) days after the invoice date via registered letter or via a proven received email to finance@askeveai.com, stating the precise nature and extent of the complaints.
### 4.4 Cancellation and Refunds
1. **Customer Cancellation**: Customer may cancel a Paid Subscription at any time by following the cancellation instructions provided in the administrative interface or by contacting Ask Eve AI. Unless otherwise stated, cancellation will take effect at the end of the billing period in which Customer cancels.
2. **No Refunds**: Ask Eve AI does not offer refunds or reimbursements for partial subscription periods unless otherwise agreed between the Parties in writing.
3. **Ask Eve AI Termination**: In addition to, and without prejudice to any other rights Ask Eve AI may have under these Terms, Ask Eve AI reserves the right to terminate a Paid Subscription at any time upon at least fourteen (14) days' notice. Unless Ask Eve AI notifies Customer otherwise, Ask Eve AI will grant Customer access to the Paid Subscription for the remainder of the then-current billing period.
### 4.5 Price Changes
Ask Eve AI may from time to time change the prices for Paid Subscriptions, including recurring Basic Fees and Additional Fees, in response to circumstances such as:
- Changes to product offerings and features
- Changes in business operations or economic environment
- Changes in costs from subcontractors or service providers
- Security, legal, or regulatory reasons
Ask Eve AI will provide reasonable notice of price changes by any reasonable means, including by email or in-app notice, which will in any event not be less than fourteen (14) days. Price changes will become effective at the start of the next subscription period following the date of the price change.
Subject to applicable law, Customer will have accepted the new price by continuing to use the Services after the new price comes into effect. If Customer does not agree to a price change, Customer may reject the change by unsubscribing from the applicable Paid Subscription before the price change comes into effect.
---
## 5. Suspension and Termination
### 5.1 Suspension for Non-Payment
1. **Basic Fees**: If Basic Fees are not paid when due, Ask Eve AI reserves the right to immediately suspend Customer's access to the Services without prior notice.
2. **Additional Fees**: If Additional Fees are not paid within thirty (30) days of the invoice date, Ask Eve AI may suspend Customer's access to the Services.
3. **Reactivation**: Suspended accounts may be reactivated upon payment of all outstanding amounts. However, time elapsed during suspension still counts toward the applicable billing period, and no pro-rata refunds or credits will be provided.
### 5.2 Immediate Termination by Ask Eve AI
Ask Eve AI reserves the right to suspend or terminate Customer's access to the Services or delete Customer's account immediately without any notice, compensation, or court intervention if Ask Eve AI determines:
1. Customer has breached these Terms, including violation of Section 1.2 (Limitations on Use of Services) or Section 1.3 (Acceptable Use and Compliance)
2. Customer becomes insolvent, files a petition of bankruptcy (or any similar petition under any insolvency law of any jurisdiction), ceases its activities, or proposes any dissolution
3. Ask Eve AI must do so to comply with applicable law
4. Customer's use of the Services could cause risk or harm to Ask Eve AI, its users, or anyone else
### 5.3 Service Discontinuation
Ask Eve AI may decide to discontinue the Services. In such case, Ask Eve AI will give Customer advance notice and a refund for any prepaid, unused Services on a pro-rata basis.
### 5.4 Data Upon Termination
1. **License Suspension**: When a subscription is suspended or cancelled, Customer loses access to the Services, but tenant data is not automatically deleted. Customer may resume access by reactivating the subscription and paying applicable fees.
2. **Tenant Termination**: Customer may request full termination of its tenant account and deletion of all associated tenant data by contacting Ask Eve AI. Upon such request:
- Tenant-specific content will be isolated and marked for deletion
- Deletion will occur within ninety (90) days as specified in the Data Protection Agreement
- Financial and billing records will be retained for seven (7) years as required by Belgian law
- User accounts will be disabled to maintain audit trail integrity
3. **Data Export**: Customer may export accessible data through the API while subscription remains active and fees are current. Ask Eve AI does not provide separate data export services.
---
## 6. Warranties and Disclaimers
### 6.1 Service Availability
Ask Eve AI strives to provide high availability of the Services but does not guarantee any specific uptime or service level. Ask Eve AI reserves the right to:
- Perform scheduled maintenance between 22:00 and 05:00 CET without prior notice
- Perform scheduled maintenance outside these hours with at least seven (7) days' advance notice
- Perform emergency maintenance at any time without notice when necessary to protect the security, integrity, or availability of the Services
### 6.2 Warranty Disclaimer
THE SERVICES ARE PROVIDED "AS IS" AND "AS AVAILABLE." TO THE FULLEST EXTENT PERMITTED BY LAW, ASK EVE AI AND ITS PARTNERS MAKE NO WARRANTY OF ANY KIND, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, INCLUDING WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
Specifically, Ask Eve AI does not warrant that:
- The Services will meet Customer's performance requirements or operate in accordance with Customer's expectations
- The Services will be uninterrupted, secure, or error-free
- Any errors or defects will be corrected
- The Services will be free from viruses or other harmful components
- Results obtained from use of the Services will be accurate or reliable
Customer acknowledges that before entering into these Terms, Customer has evaluated the Services and accepts responsibility for selection of the Services, their use, and the results to be obtained therefrom.
### 6.3 AI-Specific Disclaimers
Neither Ask Eve AI nor its partners make any warranty about:
- The accuracy, completeness, or appropriateness of any Output generated by the Services
- Any content or information in or from an end user or Customer account
- The reliability of AI models or the absence of AI hallucinations, errors, or biases
- The suitability of Output for any particular purpose or decision-making process
Customer accepts and agrees that any use of Output from the Services is at Customer's sole risk and that Customer will not rely on Output as a sole source of truth or factual information, or as a substitute for professional advice.
---
## 7. Limitation of Liability
### 7.1 Liability Cap
TO THE FULLEST EXTENT PERMITTED BY LAW, THE TOTAL AGGREGATE LIABILITY OF ASK EVE AI UNDER THESE TERMS SHALL BE LIMITED TO THE TOTAL AMOUNT OF BASIC FEES PAID BY CUSTOMER TO ASK EVE AI DURING THE THREE (3) MONTHS IMMEDIATELY PRIOR TO THE EVENT GIVING RISE TO THE LIABILITY. ADDITIONAL FEES (OVERAGE) ARE EXCLUDED FROM THIS CALCULATION.
### 7.2 Exclusion of Consequential Damages
IN NO EVENT SHALL ASK EVE AI BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, OR PUNITIVE DAMAGES, INCLUDING BUT NOT LIMITED TO:
- Loss of profits or revenue
- Loss of business or anticipated savings
- Loss of goodwill or reputation
- Loss of data or information
- Business interruption
- Cost of procurement of substitute services
- Any other indirect or consequential loss or damage
This exclusion applies regardless of the legal theory on which the claim is based (contract, tort, negligence, strict liability, or otherwise) and whether or not Ask Eve AI has been advised of the possibility of such damages.
### 7.3 Specific Exclusions
Ask Eve AI shall have no liability whatsoever for:
- **AI Output**: Any damages or claims resulting from Customer's use of, reliance on, or decisions made based on Output generated by the Services
- **Third-Party Services**: Deficiencies in infrastructure services or third-party software provided by Ask Eve AI's Sub-Processors, beyond the liability such Sub-Processors have toward Ask Eve AI
- **Customer Content**: Any claims arising from Customer's Input, including claims of infringement, defamation, or violation of privacy rights
- **End User Claims**: Claims brought by Customer's end users arising from Customer's use of the Services
- **Unauthorized Use**: Damages resulting from unauthorized access to or use of Customer's account
- **Force Majeure**: Events beyond Ask Eve AI's reasonable control, including acts of God, natural disasters, war, terrorism, riots, labor disputes, governmental actions, internet disturbances, epidemics, pandemics, or failures of third-party infrastructure providers
### 7.4 Customer Indemnification
Customer shall, at its own expense, indemnify, defend, and hold Ask Eve AI harmless from and against any claim(s), damages, losses, liabilities, costs, and expenses (including reasonable legal fees) brought against Ask Eve AI by a third party arising out of or related to:
- Customer's use of Output obtained from the Services
- Customer's breach of these Terms
- Customer's violation of any applicable laws or regulations
- Customer's violation of any third-party rights
- Customer's failure to comply with GDPR or other data protection laws
### 7.5 Mandatory Liability
Nothing in these Terms shall limit or exclude liability to the extent such limitation or exclusion is prohibited by mandatory applicable law, including liability for:
- Death or personal injury caused by negligence
- Fraud or fraudulent misrepresentation
- Intentional misconduct or gross negligence
- Any other liability that cannot be excluded or limited under Belgian or EU law
### 7.6 Basis of the Bargain
Customer acknowledges and agrees that the limitations of liability set forth in this Section 7 are fundamental elements of the basis of the bargain between Ask Eve AI and Customer, and that Ask Eve AI would not be able to provide the Services on an economically reasonable basis without these limitations.
---
## 8. Confidential Information
### 8.1 Mutual Confidentiality Obligations
1. **Ask Eve AI's Confidential Information**: Customer acknowledges that information and data (including general business information) it receives from Ask Eve AI concerning the Services and any documentation related to the Services are confidential and proprietary and a valuable commercial asset of Ask Eve AI.
2. **Customer's Confidential Information**: Ask Eve AI acknowledges that general business information and Customer data it receives from Customer is confidential and proprietary.
3. **Confidentiality Obligations**: Both Parties agree to:
- Keep confidential information received from the other Party in confidence
- Not disclose any such information to third parties without prior written consent of the disclosing Party
- Not use confidential information for its own benefit or purposes other than fulfilling contractual obligations
- Disclose confidential information only to employees or advisors who require the information to enable that Party to fulfill its contractual obligations and who are bound by similar confidentiality obligations
### 8.2 Exclusions from Confidentiality
A Party's Confidential Information shall not be deemed to include information that:
- Is or becomes publicly known other than through any act or omission of the receiving Party
- Was in the receiving Party's lawful possession before the disclosure
- Is lawfully disclosed to the receiving Party by a third party without restriction on disclosure
- Is independently developed by the receiving Party, which independent development can be shown by written evidence
- Is required to be disclosed by law, by any court of competent jurisdiction, or by any regulatory or administrative body
---
## 9. Data Protection
### 9.1 Data Protection Agreement
For commercial customers, the processing of personal data is governed by our Data Protection Agreement, which is incorporated into these Terms by reference. The Data Protection Agreement can be found at [link to DPA].
### 9.2 Precedence
In the event of any conflict between these Terms and the Data Protection Agreement regarding data protection matters, the Data Protection Agreement shall prevail.
### 9.3 Customer Responsibilities
Customer is responsible for:
- Ensuring it has a lawful basis for processing personal data through the Services
- Providing appropriate privacy notices to data subjects
- Obtaining necessary consents where required
- Responding to data subject rights requests
- Implementing appropriate technical and organizational measures for data it controls
---
## 10. General Provisions
### 10.1 Assignment
Customer may not assign any part of these Terms without Ask Eve AI's prior written consent, except that no such consent will be required with respect to an assignment of these Terms to an Affiliate or in connection with a merger, acquisition, corporate reorganization, or sale of all or substantially all of its assets. Any other attempt to transfer or assign is void.
Ask Eve AI may assign these Terms or any rights hereunder without Customer's consent.
### 10.2 Dispute Resolution
1. **Informal Negotiation**: Before initiating any formal legal proceedings, the Parties agree to first attempt to resolve any dispute, claim, or controversy arising out of or relating to these Terms through good faith negotiations for a period of thirty (30) days.
2. **Formal Proceedings**: If the dispute cannot be resolved through informal negotiation, either Party may pursue formal legal proceedings, including through a Belgian bailiff (deurwaarder/huissier de justice) or other legal collection methods available under Belgian law.
### 10.3 Governing Law and Jurisdiction
These Terms are exclusively governed by Belgian law, without regard to its conflict of laws principles. Any litigation relating to the conclusion, validity, interpretation, and/or performance of these Terms, or any other dispute concerning or related to these Terms, shall be submitted to the exclusive jurisdiction of the courts of Ghent (Gent), Belgium.
### 10.4 Severability
If any provision of these Terms is held to be void, invalid, or unenforceable under applicable law, this shall not cause the other provisions of these Terms to be void or unenforceable. In such cases, the Parties shall replace the affected provision with a different provision that is not void or unenforceable and that represents the same intention that the Parties had with the original provision.
### 10.5 Force Majeure
Neither Ask Eve AI nor Customer will be liable for inadequate performance to the extent caused by a condition that was beyond the Party's reasonable control, including but not limited to natural disaster, act of war or terrorism, riot, labor condition, governmental action, internet disturbance, epidemic, pandemic, or failure of third-party infrastructure providers.
Any delay resulting from such causes shall extend performance accordingly or excuse performance, in whole or in part, as may be reasonable under the circumstances. In such an event, each Party shall notify the other Party of the expected duration of the force majeure event.
### 10.6 Modification of Terms
1. **Notice of Changes**: Ask Eve AI reserves the right to modify these Terms at any time. We will provide reasonable notice of any material changes to these Terms by any reasonable means, including by email, in-app notification, or by posting notice of the changes on our website, which notice will in any event be provided at least fourteen (14) days before the changes take effect.
2. **Acceptance**: Customer's continued use of the Services after such modifications will constitute acceptance of the modified Terms. If Customer does not agree to the modified Terms, Customer must discontinue use of the Services and may cancel the subscription in accordance with Section 4.4.
3. **Non-Material Changes**: Ask Eve AI may make non-material changes (such as corrections of typos, clarifications, or updates to contact information) without advance notice.
### 10.7 Entire Agreement
These Terms, together with the Data Protection Agreement and any other documents expressly incorporated by reference, constitute the entire agreement between the Parties concerning the subject matter hereof and supersede all prior agreements, understandings, and arrangements, whether written or oral, relating to such subject matter.
### 10.8 No Waiver
The failure of either Party to enforce any provision of these Terms shall not constitute a waiver of that provision or any other provision. No waiver shall be effective unless made in writing and signed by an authorized representative of the waiving Party.
### 10.9 Notices
All notices required or permitted under these Terms shall be in writing and shall be deemed given:
- When delivered personally
- When sent by confirmed email to the email address provided by the receiving Party
- Three (3) business days after being sent by registered mail to the address provided by the receiving Party
Notices to Ask Eve AI should be sent to: legal@askeveai.com
### 10.10 Language
These Terms are executed in English. In case of any discrepancy between language versions, the English version shall prevail.
### 10.11 Survival
The following provisions shall survive termination or expiration of these Terms: Sections 2.2 (Ownership), 3 (Intellectual Property), 6.2 and 6.3 (Disclaimers), 7 (Limitation of Liability), 8 (Confidential Information), and 10 (General Provisions).
---
## Contact Information
For questions about these Terms, please contact:
**Ask Eve AI (Flow IT BV)**
Toekomststraat 62
9800 Deinze
Belgium
Company Number: BE0877.273.542
Email: legal@askeveai.com
Website: https://askeveai.com
---
**By using the Services, you acknowledge that you have read, understood, and agree to be bound by these Terms of Service.**
---
*Last updated: October 3, 2025*

View File

@@ -86,6 +86,7 @@ services:
- ../scripts:/app/scripts - ../scripts:/app/scripts
- ../patched_packages:/app/patched_packages - ../patched_packages:/app/patched_packages
- ./eveai_logs:/app/logs - ./eveai_logs:/app/logs
- ../db_backups:/app/db_backups
depends_on: depends_on:
db: db:
condition: service_healthy condition: service_healthy

View File

@@ -2,3 +2,5 @@ FROM registry.ask-eve-ai-local.com/josakola/eveai-base:latest
# Copy the source code into the container. # Copy the source code into the container.
COPY eveai_ops /app/eveai_ops COPY eveai_ops /app/eveai_ops
COPY migrations /app/migrations COPY migrations /app/migrations
COPY db_backups /app/db_backups

View File

@@ -70,6 +70,7 @@
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}
{{ dropdown('Tenants', 'source_environment', [ {{ dropdown('Tenants', 'source_environment', [
{'name': 'Tenants', 'url': 'user/tenants', 'roles': ['Super User', 'Partner Admin']}, {'name': 'Tenants', 'url': 'user/tenants', 'roles': ['Super User', 'Partner Admin']},
{'name': 'Consent Versions', 'url': 'user/consent_versions', 'roles': ['Super User']},
{'name': 'Tenant Overview', 'url': 'user/tenant_overview', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Tenant Overview', 'url': 'user/tenant_overview', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
{'name': 'Edit Tenant', 'url': 'user/tenant/' ~ session['tenant'].get('id'), 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Edit Tenant', 'url': 'user/tenant/' ~ session['tenant'].get('id'), 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},
{'name': 'Tenant Partner Services', 'url': 'user/tenant_partner_services', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']}, {'name': 'Tenant Partner Services', 'url': 'user/tenant_partner_services', 'roles': ['Super User', 'Partner Admin', 'Tenant Admin']},

View File

@@ -0,0 +1,75 @@
{% extends 'base.html' %}
{% from "macros.html" import render_field, render_included_field %}
{% block title %}Create or Edit Consent Version{% endblock %}
{% block content_title %}Create or Edit Consent Version{% endblock %}
{% block content_description %}Create or Edit Consent Version{% endblock %}
{% block content %}
<form method="post">
{{ form.hidden_tag() }}
{% set disabled_fields = [] %}
{% set exclude_fields = [] %}
{% for field in form %}
{{ render_field(field, disabled_fields, exclude_fields) }}
{% endfor %}
<button type="submit" class="btn btn-primary">Save Consent Version</button>
</form>
{% endblock %}
{% block content_footer %}
{% endblock %}
{% block scripts %}
<script>
// JavaScript om de gebruiker's timezone te detecteren
document.addEventListener('DOMContentLoaded', (event) => {
// Detect timezone
const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
// Send timezone to the server via a POST request
fetch('set_user_timezone', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ timezone: userTimezone })
}).then(response => {
if (response.ok) {
console.log('Timezone sent to server successfully');
} else {
console.error('Failed to send timezone to server');
}
});
$('#timezone').select2({
placeholder: 'Selecteer een timezone...',
allowClear: true,
theme: 'bootstrap',
width: '100%',
dropdownAutoWidth: true,
dropdownCssClass: 'timezone-dropdown', // Een custom class voor specifieke styling
scrollAfterSelect: false,
// Verbeterd scroll gedrag
dropdownParent: $('body')
});
// Stel de huidige waarde in als de dropdown wordt geopend
$('#timezone').on('select2:open', function() {
if ($(this).val()) {
setTimeout(function() {
let selectedOption = $('.select2-results__option[aria-selected=true]');
if (selectedOption.length) {
selectedOption[0].scrollIntoView({ behavior: 'auto', block: 'center' });
}
}, 0);
}
});
});
</script>
{% endblock %}

View File

@@ -0,0 +1,75 @@
{% extends 'base.html' %}
{% from "macros.html" import render_field, render_included_field %}
{% block title %}Create or Edit Consent Version{% endblock %}
{% block content_title %}Create or Edit Consent Version{% endblock %}
{% block content_description %}Create or Edit Consent Version{% endblock %}
{% block content %}
<form method="post">
{{ form.hidden_tag() }}
{% set disabled_fields = ["consent_type"] %}
{% set exclude_fields = [] %}
{% for field in form %}
{{ render_field(field, disabled_fields, exclude_fields) }}
{% endfor %}
<button type="submit" class="btn btn-primary">Save Consent Version</button>
</form>
{% endblock %}
{% block content_footer %}
{% endblock %}
{% block scripts %}
<script>
// JavaScript om de gebruiker's timezone te detecteren
document.addEventListener('DOMContentLoaded', (event) => {
// Detect timezone
const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
// Send timezone to the server via a POST request
fetch('set_user_timezone', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ timezone: userTimezone })
}).then(response => {
if (response.ok) {
console.log('Timezone sent to server successfully');
} else {
console.error('Failed to send timezone to server');
}
});
$('#timezone').select2({
placeholder: 'Selecteer een timezone...',
allowClear: true,
theme: 'bootstrap',
width: '100%',
dropdownAutoWidth: true,
dropdownCssClass: 'timezone-dropdown', // Een custom class voor specifieke styling
scrollAfterSelect: false,
// Verbeterd scroll gedrag
dropdownParent: $('body')
});
// Stel de huidige waarde in als de dropdown wordt geopend
$('#timezone').on('select2:open', function() {
if ($(this).val()) {
setTimeout(function() {
let selectedOption = $('.select2-results__option[aria-selected=true]');
if (selectedOption.length) {
selectedOption[0].scrollIntoView({ behavior: 'auto', block: 'center' });
}
}, 0);
}
});
});
</script>
{% endblock %}

View File

@@ -118,7 +118,7 @@ def view_content(content_type):
Show content like release notes, terms of use, etc. Show content like release notes, terms of use, etc.
Args: Args:
content_type (str): Type content (eg. 'changelog', 'terms', 'privacy') content_type (str): Type content (eg. 'changelog', 'terms', 'dpa')
""" """
try: try:
major_minor = request.args.get('version') major_minor = request.args.get('version')
@@ -135,14 +135,14 @@ def view_content(content_type):
titles = { titles = {
'changelog': 'Release Notes', 'changelog': 'Release Notes',
'terms': 'Terms & Conditions', 'terms': 'Terms & Conditions',
'privacy': 'Privacy Statement', 'dpadpa': 'Data Privacy Agreement',
# Voeg andere types toe indien nodig # Voeg andere types toe indien nodig
} }
descriptions = { descriptions = {
'changelog': 'EveAI Release Notes', 'changelog': 'EveAI Release Notes',
'terms': "Terms & Conditions for using AskEveAI's Evie", 'terms': "Terms & Conditions for using AskEveAI's Evie",
'privacy': "Privacy Statement for AskEveAI's Evie", 'dpadpa': "Data Privacy Agreement for AskEveAI's Evie",
# Voeg andere types toe indien nodig # Voeg andere types toe indien nodig
} }

View File

@@ -3,7 +3,8 @@ from flask_security import roles_accepted
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
import ast import ast
from common.models.user import Tenant, User, TenantDomain, TenantProject, TenantMake, PartnerTenant, PartnerService from common.models.user import Tenant, User, TenantDomain, TenantProject, TenantMake, PartnerTenant, PartnerService, \
ConsentVersion
from common.services.user import UserServices, PartnerServices from common.services.user import UserServices, PartnerServices
from common.utils.eveai_exceptions import EveAINoSessionPartner, EveAINoManagementPartnerService from common.utils.eveai_exceptions import EveAINoSessionPartner, EveAINoManagementPartnerService
from common.utils.security_utils import current_user_has_role from common.utils.security_utils import current_user_has_role
@@ -287,6 +288,8 @@ def get_tenant_makes_list_view(tenant_id):
# Tenant Partner Services list view helper # Tenant Partner Services list view helper
def get_tenant_partner_services_list_view(tenant_id): def get_tenant_partner_services_list_view(tenant_id):
"""Generate the tenant partner services list view configuration for a specific tenant""" """Generate the tenant partner services list view configuration for a specific tenant"""
# Get partner services for the tenant through PartnerTenant association # Get partner services for the tenant through PartnerTenant association
@@ -328,3 +331,48 @@ def get_tenant_partner_services_list_view(tenant_id):
'form_action': url_for('user_bp.tenant_partner_services'), 'form_action': url_for('user_bp.tenant_partner_services'),
'description': f'Partner Services for tenant {tenant_id}' 'description': f'Partner Services for tenant {tenant_id}'
} }
def get_consent_versions_list_view():
"""Generate the tenant makes list view configuration for a specific tenant"""
# Get makes for the tenant
query = ConsentVersion.query.filter_by().order_by(ConsentVersion.id)
consent_versions = query.all()
# Prepare data for Tabulator
data = []
for cv in consent_versions:
data.append({
'id': cv.id,
'consent_type': cv.consent_type,
'consent_version': cv.consent_version,
'consent_valid_from': cv.consent_valid_from.strftime('%Y-%m-%d') if cv.consent_valid_from else '',
'consent_valid_to': cv.consent_valid_to.strftime('%Y-%m-%d') if cv.consent_valid_to else '',
})
# Column Definitions
columns = [
{'title': 'ID', 'field': 'id', 'width': 80},
{'title': 'Consent Type', 'field': 'consent_type'},
{'title': 'From', 'field': 'consent_valid_from'},
{'title': 'To', 'field': 'consent_valid_to'}
]
actions = [
{'value': 'edit_consent_version', 'text': 'Edit Consent Version', 'class': 'btn-primary', 'requiresSelection': True},
{'value': 'create_consent_version', 'text': 'Create Consent Version', 'class': 'btn-success', 'position': 'right', 'requiresSelection': False},
]
initial_sort = [{'column': 'id', 'dir': 'asc'}]
return {
'title': 'Consent Versions',
'data': data,
'columns': columns,
'actions': actions,
'initial_sort': initial_sort,
'table_id': 'consent_versions_table',
'form_action': url_for('user_bp.handle_consent_version_selection'),
'description': f'Consent Versions'
}

View File

@@ -10,7 +10,8 @@ from datetime import datetime as dt, timezone as tz
from itsdangerous import URLSafeTimedSerializer from itsdangerous import URLSafeTimedSerializer
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from common.models.user import User from common.models.user import User, ConsentStatus
from common.services.user import TenantServices
from common.utils.eveai_exceptions import EveAIException, EveAINoActiveLicense from common.utils.eveai_exceptions import EveAIException, EveAINoActiveLicense
from common.utils.nginx_utils import prefixed_url_for from common.utils.nginx_utils import prefixed_url_for
from eveai_app.views.security_forms import SetPasswordForm, ResetPasswordForm, ForgotPasswordForm from eveai_app.views.security_forms import SetPasswordForm, ResetPasswordForm, ForgotPasswordForm
@@ -56,8 +57,24 @@ def login():
db.session.commit() db.session.commit()
if current_user.has_roles('Super User'): if current_user.has_roles('Super User'):
return redirect(prefixed_url_for('user_bp.tenants', for_redirect=True)) return redirect(prefixed_url_for('user_bp.tenants', for_redirect=True))
else: if current_user.has_roles('Partner Admin'):
return redirect(prefixed_url_for('user_bp.tenant_overview', for_redirect=True)) return redirect(prefixed_url_for('user_bp.tenants', for_redirect=True))
consent_status = TenantServices.get_consent_status(user.tenant_id)
match consent_status:
case ConsentStatus.CONSENTED:
return redirect(prefixed_url_for('user_bp.tenant_overview', for_redirect=True))
case ConsentStatus.NOT_CONSENTED:
if current_user.has_roles('Tenant Admin'):
return redirect(prefixed_url_for('user_bp.tenant_consent', for_redirect=True))
else:
return redirect(prefixed_url_for('user_bp.no_consent', for_redirect=True))
case ConsentStatus.RENEWAL_REQUIRED:
if current_user.has_roles('Tenant Admin'):
return redirect(prefixed_url_for('user_bp.tenant_consent_renewal', for_redirect=True))
else:
return redirect(prefixed_url_for('user_bp.consent_renewal', for_redirect=True))
case _:
return redirect(prefixed_url_for('basic_bp.index', for_redirect=True))
else: else:
flash('Invalid username or password', 'danger') flash('Invalid username or password', 'danger')
current_app.logger.error(f'Invalid username or password for given email: {user.email}') current_app.logger.error(f'Invalid username or password for given email: {user.email}')

View File

@@ -192,6 +192,7 @@ class TenantMakeForm(DynamicFormBase):
self.allowed_languages.choices = [(details['iso 639-1'], f"{details['flag']} {details['iso 639-1']}") self.allowed_languages.choices = [(details['iso 639-1'], f"{details['flag']} {details['iso 639-1']}")
for name, details in lang_details.items()] for name, details in lang_details.items()]
class EditTenantMakeForm(DynamicFormBase): class EditTenantMakeForm(DynamicFormBase):
id = IntegerField('ID', widget=HiddenInput()) id = IntegerField('ID', widget=HiddenInput())
name = StringField('Name', validators=[DataRequired(), Length(max=50), validate_make_name]) name = StringField('Name', validators=[DataRequired(), Length(max=50), validate_make_name])
@@ -212,5 +213,22 @@ class EditTenantMakeForm(DynamicFormBase):
self.default_language.choices = choices self.default_language.choices = choices
class ConsentVersionForm(FlaskForm):
consent_type = SelectField('Consent Type', choices=[], validators=[DataRequired()])
consent_version = StringField('Consent Version', validators=[DataRequired(), Length(max=20)])
consent_valid_from = DateField('Consent Valid From', id='form-control datepicker', validators=[DataRequired()])
consent_valid_to = DateField('Consent Valid To', id='form-control datepicker', validators=[Optional()])
def __init__(self, *args, **kwargs):
super(ConsentVersionForm, self).__init__(*args, **kwargs)
# Initialise consent types
self.consent_type.choices = [(t, t) for t in current_app.config['CONSENT_TYPES']]
class EditConsentVersionForm(FlaskForm):
consent_type = StringField('Consent Type', validators=[DataRequired()])
consent_version = StringField('Consent Version', validators=[DataRequired(), Length(max=20)])
consent_valid_from = DateField('Consent Valid From', id='form-control datepicker', validators=[DataRequired()])
consent_valid_to = DateField('Consent Valid To', id='form-control datepicker', validators=[Optional()])

View File

@@ -6,13 +6,15 @@ from flask_security import roles_accepted, current_user
from sqlalchemy.exc import SQLAlchemyError, IntegrityError from sqlalchemy.exc import SQLAlchemyError, IntegrityError
import ast import ast
from common.models.user import User, Tenant, Role, TenantDomain, TenantProject, PartnerTenant, TenantMake from common.models.user import User, Tenant, Role, TenantDomain, TenantProject, PartnerTenant, TenantMake, \
ConsentVersion
from common.extensions import db, security, minio_client, simple_encryption, cache_manager from common.extensions import db, security, minio_client, simple_encryption, cache_manager
from common.utils.dynamic_field_utils import create_default_config_from_type_config from common.utils.dynamic_field_utils import create_default_config_from_type_config
from common.utils.security_utils import send_confirmation_email, send_reset_email from common.utils.security_utils import send_confirmation_email, send_reset_email
from config.type_defs.service_types import SERVICE_TYPES from config.type_defs.service_types import SERVICE_TYPES
from .user_forms import TenantForm, CreateUserForm, EditUserForm, TenantDomainForm, TenantSelectionForm, \ from .user_forms import TenantForm, CreateUserForm, EditUserForm, TenantDomainForm, TenantSelectionForm, \
TenantProjectForm, EditTenantProjectForm, TenantMakeForm, EditTenantForm, EditTenantMakeForm TenantProjectForm, EditTenantProjectForm, TenantMakeForm, EditTenantForm, EditTenantMakeForm, ConsentVersionForm, \
EditConsentVersionForm
from common.utils.database import Database from common.utils.database import Database
from common.utils.view_assistants import prepare_table_for_macro, form_validation_failed from common.utils.view_assistants import prepare_table_for_macro, form_validation_failed
from common.utils.simple_encryption import generate_api_key from common.utils.simple_encryption import generate_api_key
@@ -25,7 +27,7 @@ from common.utils.mail_utils import send_email
from eveai_app.views.list_views.user_list_views import get_tenants_list_view, get_users_list_view, \ from eveai_app.views.list_views.user_list_views import get_tenants_list_view, get_users_list_view, \
get_tenant_domains_list_view, get_tenant_projects_list_view, get_tenant_makes_list_view, \ get_tenant_domains_list_view, get_tenant_projects_list_view, get_tenant_makes_list_view, \
get_tenant_partner_services_list_view get_tenant_partner_services_list_view, get_consent_versions_list_view
from eveai_app.views.list_views.list_view_utils import render_list_view from eveai_app.views.list_views.list_view_utils import render_list_view
user_bp = Blueprint('user_bp', __name__, url_prefix='/user') user_bp = Blueprint('user_bp', __name__, url_prefix='/user')
@@ -693,6 +695,87 @@ def tenant_partner_services():
return render_list_view('list_view.html', **config) return render_list_view('list_view.html', **config)
@user_bp.route('/consent_versions', methods=['GET', 'POST'])
@roles_accepted('Super User')
def consent_versions():
config = get_consent_versions_list_view()
return render_list_view('list_view.html', **config)
@user_bp.route('/handle_consent_version_selection', methods=['POST'])
@roles_accepted('Super User')
def handle_consent_version_selection():
action = request.form['action']
if action == 'create_consent_version':
return redirect(prefixed_url_for('user_bp.consent_version', for_redirect=True))
consent_version_identification = request.form.get('selected_row')
consent_version_id = ast.literal_eval(consent_version_identification).get('value')
if action == 'edit_consent_version':
return redirect(prefixed_url_for('user_bp.edit_consent_version', consent_version_id=consent_version_id, for_redirect=True))
# Altijd teruggaan naar de tenant_makes pagina
return redirect(prefixed_url_for('user_bp.consent_versions', for_redirect=True))
@user_bp.route('/consent_version', methods=['GET', 'POST'])
@roles_accepted('Super User')
def consent_version():
form = ConsentVersionForm()
if form.validate_on_submit():
new_consent_version = ConsentVersion()
form.populate_obj(new_consent_version)
set_logging_information(new_consent_version, dt.now(tz.utc))
try:
db.session.add(new_consent_version)
db.session.commit()
flash('Consent Version successfully added!', 'success')
current_app.logger.info(f'Consent Version {new_consent_version.consent_type}, version {new_consent_version.consent_version} successfully added ')
# Enable step 2 of creation of retriever - add configuration of the retriever (dependent on type)
return redirect(prefixed_url_for('user_bp.consent_versions', for_redirect=True))
except SQLAlchemyError as e:
db.session.rollback()
flash(f'Failed to add Consent Version. Error: {e}', 'danger')
current_app.logger.error(f'Failed to add Consent Version. Error: {str(e)}')
return render_template('user/consent_version.html', form=form)
@user_bp.route('/consent_version/<int:consent_version_id>', methods=['GET', 'POST'])
@roles_accepted('Super User')
def edit_consent_version(consent_version_id):
"""Edit an existing Consent Version."""
# Get the Consent Version or return 404
cv = ConsentVersion.query.get_or_404(consent_version_id)
# Create form instance with the tenant make
form = EditConsentVersionForm(request.form, obj=cv)
if form.validate_on_submit():
# Update basic fields
form.populate_obj(cv)
# Update logging information
update_logging_information(cv, dt.now(tz.utc))
# Save changes to database
try:
db.session.add(cv)
db.session.commit()
flash('Consent Version updated successfully!', 'success')
current_app.logger.info(f'Consent Version {cv.id} updated successfully')
except SQLAlchemyError as e:
db.session.rollback()
flash(f'Failed to update Consent Version. Error: {str(e)}', 'danger')
current_app.logger.error(f'Failed to update Consent Version {consent_version_id}. Error: {str(e)}')
return render_template('user/consent_version.html', form=form, consent_version_id=consent_version_id)
return redirect(prefixed_url_for('user_bp.consent_versions', for_redirect=True))
else:
form_validation_failed(request, form)
return render_template('user/edit_consent_version.html', form=form, consent_version_id=consent_version_id)
def reset_uniquifier(user): def reset_uniquifier(user):
security.datastore.set_uniquifier(user) security.datastore.set_uniquifier(user)

View File

@@ -110,7 +110,7 @@ export function useContentModal() {
throw new Error(data.error || 'Onbekende fout bij het laden van content'); throw new Error(data.error || 'Onbekende fout bij het laden van content');
} }
} else if (data.content !== undefined) { } else if (data.content !== undefined) {
// Legacy format without success property (current privacy/terms endpoints) // Legacy format without success property (current dpa/terms endpoints)
modalState.content = data.content || ''; modalState.content = data.content || '';
modalState.version = data.version || ''; modalState.version = data.version || '';
} else if (data.error) { } else if (data.error) {

View File

@@ -24,19 +24,19 @@ export default {
props: { props: {
template: { type: String, required: true }, template: { type: String, required: true },
asButton: { type: Boolean, default: false }, asButton: { type: Boolean, default: false },
ariaPrivacy: { type: String, default: 'Open privacy statement in a dialog' }, ariaPrivacy: { type: String, default: 'Open Data Privacy Agreement in a dialog' },
ariaTerms: { type: String, default: 'Open terms and conditions in a dialog' } ariaTerms: { type: String, default: 'Open Terms and Conditions in a dialog' }
}, },
emits: ['open-privacy', 'open-terms'], emits: ['open-dpa', 'open-terms'],
computed: { computed: {
linkTag() { linkTag() {
return this.asButton ? 'button' : 'a'; return this.asButton ? 'button' : 'a';
}, },
nodes() { nodes() {
// Parse only allowed tags <privacy>...</privacy> and <terms>...</terms> // Parse only allowed tags <dpa>...</dpa> and <terms>...</terms>
const source = (this.template || ''); const source = (this.template || '');
// 2) parse only allowed tags <privacy>...</privacy> and <terms>...</terms> // 2) parse only allowed tags <dpa>...</dpa> and <terms>...</terms>
const pattern = /<(privacy|terms)>([\s\S]*?)<\/\1>/gi; const pattern = /<(privacy|terms)>([\s\S]*?)<\/\1>/gi;
const out = []; const out = [];
let lastIndex = 0; let lastIndex = 0;
@@ -48,9 +48,9 @@ export default {
out.push({ type: 'text', text: source.slice(lastIndex, start) }); out.push({ type: 'text', text: source.slice(lastIndex, start) });
} }
out.push({ out.push({
type: tag, // 'privacy' | 'terms' type: tag, // 'dpa' | 'terms'
label: (label || '').trim(), label: (label || '').trim(),
aria: tag === 'privacy' ? this.ariaPrivacy : this.ariaTerms aria: tag === 'dpa' ? this.ariaPrivacy : this.ariaTerms
}); });
lastIndex = start + full.length; lastIndex = start + full.length;
} }
@@ -62,7 +62,7 @@ export default {
}, },
methods: { methods: {
emitClick(kind) { emitClick(kind) {
if (kind === 'privacy') this.$emit('open-privacy'); if (kind === 'dpa') this.$emit('open-dpa');
if (kind === 'terms') this.$emit('open-terms'); if (kind === 'terms') this.$emit('open-terms');
} }
} }

View File

@@ -480,7 +480,7 @@ export default {
// Modal handling methods // Modal handling methods
openPrivacyModal() { openPrivacyModal() {
this.loadContent('privacy'); this.loadContent('dpa');
}, },
openTermsModal() { openTermsModal() {
@@ -494,15 +494,15 @@ export default {
retryLoad() { retryLoad() {
// Retry loading the last requested content type // Retry loading the last requested content type
const currentTitle = this.contentModal.modalState.title.toLowerCase(); const currentTitle = this.contentModal.modalState.title.toLowerCase();
if (currentTitle.includes('privacy')) { if (currentTitle.includes('dpa')) {
this.loadContent('privacy'); this.loadContent('dpa');
} else if (currentTitle.includes('terms')) { } else if (currentTitle.includes('terms')) {
this.loadContent('terms'); this.loadContent('terms');
} }
}, },
async loadContent(contentType) { async loadContent(contentType) {
const title = contentType === 'privacy' ? 'Privacy Statement' : 'Terms & Conditions'; const title = contentType === 'dpa' ? 'Data Privacy Agreement' : 'Terms & Conditions';
const contentUrl = `${this.apiPrefix}/${contentType}`; const contentUrl = `${this.apiPrefix}/${contentType}`;
// Use the composable to show modal and load content // Use the composable to show modal and load content

View File

@@ -104,12 +104,12 @@
> >
<!-- Regular checkbox label --> <!-- Regular checkbox label -->
<span v-if="!isConsentField" class="checkbox-text">{{ field.name }}</span> <span v-if="!isConsentField" class="checkbox-text">{{ field.name }}</span>
<!-- Consent field with privacy and terms links (rich, multilingual) --> <!-- Consent field with dpa and terms links (rich, multilingual) -->
<ConsentRichText <ConsentRichText
v-else v-else
class="checkbox-text consent-text" class="checkbox-text consent-text"
:template="texts.consentRich" :template="texts.consentRich"
:aria-privacy="texts.ariaPrivacy || 'Open privacy statement in a dialog'" :aria-privacy="texts.ariaPrivacy || 'Open dpa statement in a dialog'"
:aria-terms="texts.ariaTerms || 'Open terms and conditions in a dialog'" :aria-terms="texts.ariaTerms || 'Open terms and conditions in a dialog'"
@open-privacy="openPrivacyModal" @open-privacy="openPrivacyModal"
@open-terms="openTermsModal" @open-terms="openTermsModal"
@@ -203,12 +203,12 @@ export default {
default: null default: null
} }
}, },
emits: ['update:modelValue', 'open-privacy-modal', 'open-terms-modal', 'keydown-enter'], emits: ['update:modelValue', 'open-dpa-modal', 'open-terms-modal', 'keydown-enter'],
setup() { setup() {
// Consent text constants (English base) - rich template // Consent text constants (English base) - rich template
const consentTexts = { const consentTexts = {
consentRich: "I agree with the <privacy>privacy statement</privacy> and the <terms>terms and conditions</terms>", consentRich: "I agree with the <dpa>dpa statement</dpa> and the <terms>terms and conditions</terms>",
ariaPrivacy: 'Open privacy statement in a dialog', ariaPrivacy: 'Open dpa statement in a dialog',
ariaTerms: 'Open terms and conditions in a dialog' ariaTerms: 'Open terms and conditions in a dialog'
}; };
@@ -259,8 +259,8 @@ export default {
// 4) Ultimate fallback (should not happen): provide a safe default // 4) Ultimate fallback (should not happen): provide a safe default
return { return {
consentRich: "I agree with the <privacy>privacy statement</privacy> and the <terms>terms and conditions</terms>", consentRich: "I agree with the <dpa>dpa statement</dpa> and the <terms>terms and conditions</terms>",
ariaPrivacy: 'Open privacy statement in a dialog', ariaPrivacy: 'Open dpa statement in a dialog',
ariaTerms: 'Open terms and conditions in a dialog' ariaTerms: 'Open terms and conditions in a dialog'
}; };
}, },
@@ -332,7 +332,7 @@ export default {
} }
}, },
openPrivacyModal() { openPrivacyModal() {
this.$emit('open-privacy-modal'); this.$emit('open-dpa-modal');
}, },
openTermsModal() { openTermsModal() {
this.$emit('open-terms-modal'); this.$emit('open-terms-modal');

View File

@@ -387,35 +387,35 @@ def translate():
@chat_bp.route('/privacy', methods=['GET']) @chat_bp.route('/privacy', methods=['GET'])
def privacy_statement(): def privacy_statement():
""" """
Public AJAX endpoint for privacy statement content Public AJAX endpoint for dpa statement content
Returns JSON response suitable for modal display Returns JSON response suitable for modal display
""" """
try: try:
# Use content_manager to get the latest privacy content # Use content_manager to get the latest dpa content
content_data = content_manager.read_content('privacy') content_data = content_manager.read_content('dpa')
if not content_data: if not content_data:
current_app.logger.error("Privacy statement content not found") current_app.logger.error("Data Privacy Agreement content not found")
return jsonify({ return jsonify({
'error': 'Privacy statement not available', 'error': 'Data Privacy Agreement not available',
'message': 'The privacy statement could not be loaded at this time.' 'message': 'The Data Pdpa Agreement could not be loaded at this time.'
}), 404 }), 404
current_app.logger.debug(f"Content data: {content_data}") current_app.logger.debug(f"Content data: {content_data}")
# Return JSON response for AJAX consumption # Return JSON response for AJAX consumption
return jsonify({ return jsonify({
'title': 'Privacy Statement', 'title': 'Data Privacy Agreement',
'content': content_data['content'], 'content': content_data['content'],
'version': content_data['version'], 'version': content_data['version'],
'content_type': content_data['content_type'] 'content_type': content_data['content_type']
}), 200 }), 200
except Exception as e: except Exception as e:
current_app.logger.error(f"Error loading privacy statement: {str(e)}") current_app.logger.error(f"Error loading Data Privacy Agreement: {str(e)}")
return jsonify({ return jsonify({
'error': 'Server error', 'error': 'Server error',
'message': 'An error occurred while loading the privacy statement.' 'message': 'An error occurred while loading the Data Privacy Agreement.'
}), 500 }), 500

View File

@@ -92,7 +92,7 @@ NO_CONTACT_DATA_QUESTIONS = [
"Unfortunately, we can only move forward if you provide your contact details. Would you still consider sharing them with us?", "Unfortunately, we can only move forward if you provide your contact details. Would you still consider sharing them with us?",
"Its totally your choice, of course. But without your contact details, we cant proceed further. Would you be open to sharing them?", "Its totally your choice, of course. But without your contact details, we cant proceed further. Would you be open to sharing them?",
"Wed love to keep going, but we can only do so if we have your contact details. Would you like to provide them now?", "Wed love to keep going, but we can only do so if we have your contact details. Would you like to provide them now?",
"Your privacy matters, and we respect your decision. Just know that without your contact details, well need to end the process here. Still interested in moving forward?", "Your dpa matters, and we respect your decision. Just know that without your contact details, well need to end the process here. Still interested in moving forward?",
"Its a shame to stop here, but we do need your contact info to proceed. Would you like to share it so we can continue?" "Its a shame to stop here, but we do need your contact info to proceed. Would you like to share it so we can continue?"
] ]
CONTACT_DATA_PROCESSED_MESSAGE = "Thank you for allowing us to contact you." CONTACT_DATA_PROCESSED_MESSAGE = "Thank you for allowing us to contact you."

View File

@@ -147,7 +147,7 @@ NO_CONTACT_DATA_QUESTIONS = [
"Unfortunately, we can only move forward if you provide your contact details. Would you still consider sharing them with us?", "Unfortunately, we can only move forward if you provide your contact details. Would you still consider sharing them with us?",
"Its totally your choice, of course. But without your contact details, we cant proceed further. Would you be open to sharing them?", "Its totally your choice, of course. But without your contact details, we cant proceed further. Would you be open to sharing them?",
"Wed love to keep going, but we can only do so if we have your contact details. Would you like to provide them now?", "Wed love to keep going, but we can only do so if we have your contact details. Would you like to provide them now?",
"Your privacy matters, and we respect your decision. Just know that without your contact details, well need to end the process here. Still interested in moving forward?", "Your dpa matters, and we respect your decision. Just know that without your contact details, well need to end the process here. Still interested in moving forward?",
"Its a shame to stop here, but we do need your contact info to proceed. Would you like to share it so we can continue?" "Its a shame to stop here, but we do need your contact info to proceed. Would you like to share it so we can continue?"
] ]
CONTACT_DATA_QUESTIONS = [ CONTACT_DATA_QUESTIONS = [

View File

@@ -0,0 +1,51 @@
"""TenantConsent model creation
Revision ID: 411f5593460e
Revises: 057fb975f0e3
Create Date: 2025-10-09 07:32:04.598209
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '411f5593460e'
down_revision = '057fb975f0e3'
branch_labels = None
depends_on = None
def upgrade():
op.create_table('tenant_consent',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('partner_id', sa.Integer(), nullable=False),
sa.Column('partner_service_id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('consent_type', sa.String(length=50), nullable=False),
sa.Column('consent_date', sa.DateTime(), server_default=sa.text('now()'), nullable=False),
sa.Column('consent_dpa_version', sa.String(length=20), nullable=False),
sa.Column('consent_t_c_version', sa.String(length=20), nullable=False),
sa.Column('consent_data', sa.JSON(), nullable=False),
sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False),
sa.Column('created_by', sa.Integer(), nullable=True),
sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False),
sa.Column('updated_by', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['created_by'], ['public.user.id'], ),
sa.ForeignKeyConstraint(['partner_id'], ['public.partner.id'], ),
sa.ForeignKeyConstraint(['partner_service_id'], ['public.partner_service.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['public.tenant.id'], ),
sa.ForeignKeyConstraint(['updated_by'], ['public.user.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['public.user.id'], ),
sa.PrimaryKeyConstraint('id'),
schema='public'
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('tenant_consent', schema='public')
# ### end Alembic commands ###

View File

@@ -0,0 +1,36 @@
"""Add ConsentVersion model
Revision ID: 8bfd440079a5
Revises: 411f5593460e
Create Date: 2025-10-09 14:12:41.318538
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '8bfd440079a5'
down_revision = '411f5593460e'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('consent_version',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('consent_type', sa.String(length=50), nullable=False),
sa.Column('consent_version', sa.String(length=20), nullable=False),
sa.Column('consent_valid_from', sa.DateTime(), server_default=sa.text('now()'), nullable=False),
sa.Column('consent_valid_to', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id'),
schema='public'
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('consent_version', schema='public')
# ### end Alembic commands ###

View File

@@ -0,0 +1,37 @@
"""Adding Tracking information to ConsentVersion
Revision ID: f5f1a8b8e238
Revises: 8bfd440079a5
Create Date: 2025-10-09 15:30:00.046174
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'f5f1a8b8e238'
down_revision = '8bfd440079a5'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('consent_version', schema=None) as batch_op:
batch_op.add_column(sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False))
batch_op.add_column(sa.Column('created_by', sa.Integer(), nullable=True))
batch_op.add_column(sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False))
batch_op.add_column(sa.Column('updated_by', sa.Integer(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('consent_version', schema=None) as batch_op:
batch_op.drop_column('updated_by')
batch_op.drop_column('updated_at')
batch_op.drop_column('created_by')
batch_op.drop_column('created_at')
# ### end Alembic commands ###

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# Configuration # Configuration
DEV_DB_HOST="localhost" DEV_DB_HOST="db"
DEV_DB_PORT="5432" DEV_DB_PORT="5432"
DEV_DB_NAME="eveai" DEV_DB_NAME="eveai"
DEV_DB_USER="luke" DEV_DB_USER="luke"