security: sanitize hardcoded values with placeholders for public repo

This commit is contained in:
corrado.mulas
2026-04-06 14:03:15 +02:00
commit fbdd3574b3
17 changed files with 656 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

3
Dockerfile Normal file
View File

@@ -0,0 +1,3 @@
FROM nginx:alpine
COPY ./frontend /usr/share/nginx/html
#COPY ./nginx.conf /etc/nginx/nginx.conf

28
LICENSE Normal file
View File

@@ -0,0 +1,28 @@
BSD 3-Clause License
Copyright (c) 2026, overleaf-registration-fe contributors
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

153
README.md Normal file
View File

@@ -0,0 +1,153 @@
# overleaf-registration-fe
Frontend signup interface for controlled Overleaf account registration.
Static HTML/JS/CSS site that presents a registration form with domain validation, Cloudflare Turnstile CAPTCHA, and Terms of Service acceptance. Served via nginx and designed to work with the [overleaf-registration-be](https://github.com/ssep1ol/overleaf-registration-be) backend API.
## Features
- Clean, responsive signup form
- Client-side email domain validation
- Cloudflare Turnstile CAPTCHA integration
- Terms of Service modal with acceptance checkbox
- Privacy policy page
- Static site served via nginx
- Docker container deployment
## How It Works
1. User visits signup page.
2. User enters email address (validated client-side against hardcoded domain allowlist).
3. User completes Cloudflare Turnstile CAPTCHA challenge.
4. User accepts Terms of Service and Privacy Policy.
5. Form submits JSON payload to backend API endpoint.
6. User sees success/error message based on backend response.
## Tech Stack
- Vanilla JavaScript (no framework)
- HTML5 with semantic markup
- CSS (with Bootstrap-style classes from Overleaf theme)
- Cloudflare Turnstile
- nginx (Alpine-based container)
## Project Structure
```
frontend/
├── index.html # Main signup page
├── script.js # Form validation and submission logic
├── privacy.html # Privacy policy page
├── main-style-*.css # Compiled CSS from Overleaf theme
├── style.css # Additional custom styles
├── styles.css # Additional custom styles
├── scripts.js # Additional JS (if any)
└── fonts/ # Web fonts (Lato, Merriweather, Font Awesome)
nginx.conf # nginx server configuration
Dockerfile # Container image build instructions
```
## Configuration
**REQUIRED:** Before deploying, you must configure the following hardcoded values in the source files.
### 1. Cloudflare Turnstile Site Key
In `frontend/index.html`, replace the placeholder:
```html
<div class="cf-turnstile" data-sitekey="YOUR_TURNSTILE_SITE_KEY_HERE"></div>
```
Change `YOUR_TURNSTILE_SITE_KEY_HERE` to your actual Turnstile site key from the Cloudflare dashboard.
### 2. Allowed Email Domains
In `frontend/script.js`, update the `allowedDomains` array:
```javascript
const allowedDomains = ['example.edu', 'university.edu']; // CONFIGURE: Replace with your allowed email domains
```
Replace with your institution's email domains (e.g., `['studenti.polito.it', 'edu.unito.it']`).
### 3. Backend API Endpoint
In `frontend/script.js`, update the fetch URL:
```javascript
const response = await fetch('https://YOUR_BACKEND_API_URL/signup', { // CONFIGURE: Replace with your backend API endpoint
```
Replace `YOUR_BACKEND_API_URL` with your actual backend service URL.
### Summary of Required Changes
| File | Line/Element | What to Replace |
|------|--------------|-----------------|
| `frontend/index.html` | Turnstile div | `YOUR_TURNSTILE_SITE_KEY_HERE` → your site key |
| `frontend/script.js` | `allowedDomains` array | `['example.edu', ...]` → your domains |
| `frontend/script.js` | `fetch()` URL | `YOUR_BACKEND_API_URL` → your backend URL |
## Hardcoded Values in script.js
The following values are embedded in the client-side JavaScript and must be configured before deployment:
- **Allowed email domains:** Set in `allowedDomains` array
- **Backend API endpoint:** Set in `fetch()` call
- **Turnstile Site Key:** Set in `index.html` `data-sitekey` attribute
## Run Locally
Serve the `frontend/` directory with any web server.
Example with Python:
```bash
cd frontend
python3 -m http.server 8080
```
Then visit `http://localhost:8080`.
## Run With Docker
Build image:
```bash
docker build -t overleaf-registration-fe .
```
Run container:
```bash
docker run --rm -p 8080:80 overleaf-registration-fe
```
Then visit `http://localhost:8080`.
## Deployment Notes
- **Configuration Required:** You must update the placeholders in `frontend/index.html` and `frontend/script.js` before deploying (see Configuration section above).
- nginx serves static files from `/usr/share/nginx/html`.
- All routes fall back to `index.html` for single-page behavior.
- No environment variables or build-time configuration needed - all config is hardcoded in client-side files.
- After configuration, rebuild the Docker image to include your changes.
## Terms of Service
The embedded ToS modal enforces:
- Non-commercial use only
- No copyrighted material without authorization
- No NSFW, gore, or illegal content
- Users must backup their own work
- No guarantees on service availability or data safety
- Service maintained by volunteers on best-effort basis
## Privacy Policy
Located at `frontend/privacy.html` and accessible via link in signup form.
## License
This project is licensed under the BSD 3-Clause License - see the [LICENSE](LICENSE) file for details.

BIN
frontend/.DS_Store vendored Normal file

Binary file not shown.

125
frontend/index.html Normal file
View File

@@ -0,0 +1,125 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>AppuntiPolito - Overleaf SignUp</title>
<!-- Include the provided CSS -->
<link rel="stylesheet" href="register/main-style-f17f58e57337cfb154da.css">
<!-- Include Cloudflare Turnstile -->
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<script src="register/script.js"></script>
</head>
<body>
<!-- Navbar -->
<nav class="navbar navbar-default navbar-main">
<div class="container-fluid">
<div class="navbar-header">
<button class="navbar-toggle">
<i class="fa fa-bars" aria-hidden="true"></i>
</button>
<a class="navbar-title" href="/" aria-label="Overleaf AppuntiPolito">Overleaf AppuntiPolito</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li><a href="/register">Sign Up</a></li>
<li><a href="/login">Sign In</a></li>
</ul>
</div>
</div>
</nav>
<!-- Signup Form -->
<main class="content content-alt">
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4">
<div class="card">
<div class="page-header">
<h1>AppuntiPolito Overleaf Sign Up</h1>
<p>You must use your <b>@studenti.polito.it</b> or <b>@edu.unito.it</b> email.</p>
</div>
<form id="registration-form">
<div class="form-group">
<input
class="form-control"
type="email"
name="email"
placeholder="your.email@allowed-domain.it"
required
>
</div>
<!-- Add Turnstile widget -->
<div class="cf-turnstile" data-sitekey="YOUR_TURNSTILE_SITE_KEY_HERE"></div>
<div class="form-group">
<div>
<input type="checkbox" id="agree-tos" name="agree-tos" required>
<label for="agree-tos">
I have read and agree to the
<a href="#" id="open-tos-modal">Terms of Service</a> and the <a href="register/privacy.html" target="_blank">Privacy Policy</a>.
</label>
</div>
</div>
<div class="actions">
<button class="btn-primary btn" type="submit">
Sign Up
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</main>
<!-- ToS Modal -->
<div id="tos-modal" class="modal">
<div class="modal-content">
<span id="close-tos-modal" class="close">&times;</span>
<h2>Terms of Service</h2>
<p>Welcome to the AppuntiPolito Overleaf platform, a service provided and maintained by volunteers on a best-effort basis. By using this service, you agree to the following terms:</p>
<h3>1. Permitted Use</h3>
<ul>
<li><strong>No Commercial Use:</strong> This service is strictly for personal and educational purposes. Commercial use is not permitted.</li>
<li><strong>No Copyrighted Material:</strong> Uploading, sharing, or storing copyrighted materials without proper authorization is strictly prohibited.</li>
<li><strong>No NSFW, Gore, or other Illegal Content:</strong> Any content that is obscene, offensive, related to drugs, promotes violence, or violates the law is strictly forbidden.</li>
</ul>
<h3>2. User Responsibility</h3>
<ul>
<li><strong>Backup Your Work:</strong> Users are responsible for keeping backups of their work. This platform provides no guarantees for data safety or service availability.</li>
<li><strong>Self-Reliance:</strong> The platform is provided as-is, without any guarantees. Users assume all responsibility for the consequences of using the service.</li>
<li><strong>Academic Focus:</strong> This service is designed to overcome the limitations of the SaaS Overleaf offering (e.g., compile minutes) by hosting a community-driven Overleaf instance.</li>
</ul>
<h3>3. Service Availability and Liability</h3>
<p><strong>No Guarantees:</strong> The service is maintained by volunteers on a best-effort basis. We do not guarantee uptime, functionality, or data safety.</p>
<p><strong>No Liability:</strong> The staff is not responsible for any loss of data, unavailability of the service, or other issues. Use this service at your own risk.</p>
<p><strong>Volunteers Only:</strong> This is a free community service. As such, there is no formal support or warranty.</p>
<p class="important">
<strong>IMPORTANT:</strong> Keep a backup of your work on a personal storage device or cloud service. Do not rely on this platform as the sole location for your data.
</p>
<h3>4. Abuse and Termination</h3>
<ul>
<li><strong>Abuse Policy:</strong> Any user found engaging in prohibited activities or violating the rules may have their access revoked without prior notice.</li>
<li><strong>Service Shutdown:</strong> The staff reserves the right to discontinue the service at any time, especially in cases of repeated abuse by the user base.</li>
<li><strong>Law Enforcement:</strong> In cases of illegal activity, the staff will cooperate with law enforcement authorities and provide any necessary information.</li>
</ul>
<h3>5. Acceptance of Terms</h3>
<p>By using this service, you acknowledge that you have read and understood these terms. Continued use of the platform constitutes your agreement to these terms.</p>
<p>If you have questions or concerns regarding these terms, please contact the staff. Thank you for respecting the community and helping us maintain a safe and productive environment for everyone.</p>
</div>
</div>
<footer class="site-footer"><div class="site-footer-content hidden-print"><div class="row"><ul class="col-md-9"><li>&copy; 2024
<a href="https://www.overleaf.com/for/enterprises">Powered by Overleaf</a></li><li><strong class="text-muted">|</strong></li><li>Hosted and maintained by <a href="mailto:ito@lynxcloud.it">lynxcloud OdV - Turin</a> &copy; 2024</li></ul><ul class="col-md-3 text-right"><li><a href="https://github.com/overleaf/overleaf"><i class="fa fa-github-square"></i> Fork on GitHub!</a></li></ul></div></div></footer>
</body>
</html>

File diff suppressed because one or more lines are too long

118
frontend/privacy.html Normal file
View File

@@ -0,0 +1,118 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Privacy Policy - AppuntiPolito Overleaf</title>
<link rel="stylesheet" href="main-style-f17f58e57337cfb154da.css">
</head>
<body>
<nav class="navbar navbar-default navbar-main">
<div class="container-fluid">
<div class="navbar-header">
<button class="navbar-toggle">
<i class="fa fa-bars" aria-hidden="true"></i>
</button>
<a class="navbar-title" href="/" aria-label="Overleaf AppuntiPolito">Overleaf AppuntiPolito</a>
</div>
</div>
</nav>
<main class="content">
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="card">
<div class="page-header">
<h1>Privacy Policy / Politica sulla Privacy</h1>
<p>Effective Date / Data di entrata in vigore: <strong>23/11/2024</strong></p>
</div>
<!-- ENGLISH SECTION -->
<div id="english-policy">
<h2>Privacy Policy</h2>
<h3>1. Data Controller</h3>
<p><strong>Name:</strong> lynxcloud OdV</p>
<p><strong>Address:</strong> Corso Racconigi 25/9 - 10139 Torino (TO) - Italy</p>
<p><strong>Email:</strong> ito@lynxcloud.it</p>
<h3>2. Legal Basis for Processing</h3>
<p>We process your data based on GDPR's legal bases, including performance of a contract, legal obligations, and legitimate interests.</p>
<h3>3. Data We Collect</h3>
<ul>
<li>Email address</li>
<li>IP address</li>
<li>Project data</li>
</ul>
<h3>4. Data Storage and Retention</h3>
<p>Your data is stored in compliance with GDPR and deleted when no longer required.</p>
<h3>5. Your Rights</h3>
<ul>
<li>Access your data</li>
<li>Request data deletion</li>
<li>Restrict data processing</li>
</ul>
<h3>6. Cookies and Security</h3>
<p>We use minimal cookies and follow strict security practices to safeguard your data.</p>
<h3>7. Service Availability and Liability</h3>
<p>This service is offered on a best-effort basis with no guarantees of uptime or data recovery. You must maintain your own backups.</p>
<h3>8. Data Transfers Outside the EU</h3>
<p>Any transfers outside the EU will comply with GDPR using appropriate safeguards.</p>
<h3>Contact</h3>
<p>Email: ito@lynxcloud.it</p>
</div>
<hr>
<!-- ITALIAN SECTION -->
<div id="italian-policy">
<h2>Politica sulla Privacy</h2>
<h3>1. Titolare del Trattamento</h3>
<p><strong>Nome:</strong> lynxcloud OdV</p>
<p><strong>Indirizzo:</strong> Corso Racconigi 25/9 - 10139 Torino (TO) - Italy</p>
<p><strong>Email:</strong> ito@lynxcloud.it</p>
<h3>2. Base Giuridica per il Trattamento</h3>
<p>Trattiamo i tuoi dati secondo le basi giuridiche del GDPR, inclusi l'esecuzione di un contratto, obblighi legali e interessi legittimi.</p>
<h3>3. Dati che Raccogliamo</h3>
<ul>
<li>Indirizzo email</li>
<li>Indirizzo IP</li>
<li>Dati dei progetti</li>
</ul>
<h3>4. Conservazione e Durata dei Dati</h3>
<p>I tuoi dati sono conservati in conformità con il GDPR e cancellati quando non più necessari.</p>
<h3>5. I tuoi Diritti</h3>
<ul>
<li>Accedere ai tuoi dati</li>
<li>Richiedere la cancellazione dei dati</li>
<li>Limitare il trattamento dei dati</li>
</ul>
<h3>6. Cookie e Sicurezza</h3>
<p>Utilizziamo cookie minimi e seguiamo pratiche di sicurezza rigorose per proteggere i tuoi dati.</p>
<h3>7. Disponibilità del Servizio e Responsabilità</h3>
<p>Questo servizio è offerto su base "best-effort" senza garanzie di uptime o recupero dei dati. Devi mantenere copie di backup dei tuoi lavori.</p>
<h3>8. Trasferimenti di Dati al di Fuori dell'UE</h3>
<p>Eventuali trasferimenti al di fuori dell'UE saranno conformi al GDPR utilizzando adeguate garanzie.</p>
<h3>Contatti</h3>
<p>Email: ito@lynxcloud.it</p>
</div>
</div>
</div>
</div>
</div>
</main>
</body>
</html>

100
frontend/script.js Normal file
View File

@@ -0,0 +1,100 @@
document.addEventListener('DOMContentLoaded', () => {
console.log('Turnstile script loaded.');
const form = document.getElementById('registration-form');
const tosModal = document.getElementById('tos-modal');
const openTosModal = document.getElementById('open-tos-modal');
const closeTosModal = document.getElementById('close-tos-modal');
const tosCheckbox = document.querySelector('input[name="agree-tos"]');
const allowedDomains = ['example.edu', 'university.edu']; // CONFIGURE: Replace with your allowed email domains
// Automatically show the Terms of Service modal on page load
if (tosModal) {
tosModal.style.display = 'block';
}
// Handle Terms of Service modal
if (openTosModal && tosModal) {
openTosModal.addEventListener('click', (event) => {
event.preventDefault();
tosModal.style.display = 'block';
});
}
if (closeTosModal && tosModal) {
closeTosModal.addEventListener('click', () => {
tosModal.style.display = 'none';
//if (tosCheckbox) tosCheckbox.checked = true; // Auto-check ToS checkbox
});
}
window.addEventListener('click', (event) => {
if (event.target === tosModal) {
tosModal.style.display = 'none';
//if (tosCheckbox) tosCheckbox.checked = true; // Auto-check ToS checkbox
}
});
// Ensure the form exists
if (!form) {
console.error('Form not found!');
return;
}
form.addEventListener('submit', async (event) => {
event.preventDefault();
const emailInput = document.querySelector('input[name="email"]');
const email = emailInput ? emailInput.value.trim() : '';
if (!email) {
alert('Email is required.');
return;
}
// Validate email
const emailDomain = email.split('@')[1];
if (!allowedDomains.includes(emailDomain)) {
alert(`You must use a valid email from the following domains: ${allowedDomains.join(', ')}.`);
return;
}
// Ensure the Terms of Service checkbox is checked
if (!tosCheckbox || !tosCheckbox.checked) {
alert('You must agree to the Terms of Service and the Privacy Policy to continue.');
return;
}
// Retrieve Turnstile token
const turnstileResponseElement = document.querySelector('input[name="cf-turnstile-response"]');
const captchaToken = turnstileResponseElement ? turnstileResponseElement.value : '';
if (!captchaToken) {
console.error('CAPTCHA token is missing.');
alert('Please complete the CAPTCHA.');
return;
}
console.log('Submitting with Turnstile token:', captchaToken);
try {
const response = await fetch('https://YOUR_BACKEND_API_URL/signup', { // CONFIGURE: Replace with your backend API endpoint
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, captcha: captchaToken }),
});
if (response.ok) {
const result = await response.json();
alert(result.message || 'Registration successful! Check your email to set your password.');
emailInput.value = ''; // Clear the form
} else {
const error = await response.text();
console.error('Backend error:', error);
alert(`Registration failed: ${error}`);
}
} catch (error) {
console.error('Error during registration:', error);
alert('An error occurred. Please try again.');
}
});
});

5
frontend/scripts.js Normal file

File diff suppressed because one or more lines are too long

31
frontend/style.css Normal file
View File

@@ -0,0 +1,31 @@
body {
font-family: "Roboto", sans-serif;
background-color: #f8f9fa;
}
.navbar-brand {
font-weight: 700;
}
.card {
border: 1px solid #ddd;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.btn-success {
background-color: #28a745;
border-color: #28a745;
}
.btn-success:hover {
background-color: #218838;
border-color: #1e7e34;
}
footer {
position: fixed;
bottom: 10px;
width: 100%;
text-align: center;
}

5
frontend/styles.css Normal file

File diff suppressed because one or more lines are too long

17
nginx.conf Normal file
View File

@@ -0,0 +1,17 @@
server {
listen 80;
server_name _;
# Serve the frontend static files
root /usr/share/nginx/html;
index index.html;
# Serve all requests as static files
location / {
try_files $uri /index.html;
}
# Logs for debugging (optional)
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
}