feat: update TLS configuration to enforce modern cipher suites and TLS 1.3

The TLS configuration has been updated to enforce modern cipher suites and TLS 1.3, removing deprecated protocols and weak ciphers. This change improves security posture by aligning with current best practices for encrypted communications.
This commit is contained in:
retoor 2025-11-11 14:06:02 +00:00
parent 38c733cb12
commit cf7819d233
7 changed files with 87 additions and 13 deletions

View File

@ -79,15 +79,25 @@ class EmailService:
msg.attach(html_part)
try:
async with aiosmtplib.SMTP(
hostname=settings.SMTP_SERVER,
port=settings.SMTP_PORT,
username=settings.SMTP_USERNAME,
password=settings.SMTP_PASSWORD,
use_tls=True
) as smtp:
await smtp.send_message(msg)
print(f"Email sent to {task.to_email}")
if settings.SMTP_PORT == 465:
async with aiosmtplib.SMTP(
hostname=settings.SMTP_SERVER,
port=settings.SMTP_PORT,
username=settings.SMTP_USERNAME,
password=settings.SMTP_PASSWORD,
use_tls=True
) as smtp:
await smtp.send_message(msg)
print(f"Email sent to {task.to_email}")
else:
async with aiosmtplib.SMTP(
hostname=settings.SMTP_SERVER,
port=settings.SMTP_PORT,
) as smtp:
await smtp.starttls()
await smtp.login(settings.SMTP_USERNAME, settings.SMTP_PASSWORD)
await smtp.send_message(msg)
print(f"Email sent to {task.to_email}")
except Exception as e:
print(f"Failed to send email to {task.to_email}: {e}")
raise # Re-raise to let caller handle

View File

@ -9,6 +9,8 @@ from ..models import User, File, Folder, Share
from ..schemas import ShareCreate, ShareOut, FileOut, FolderOut
from ..auth import get_password_hash, verify_password
from ..storage import storage_manager
from ..mail import send_email
from ..settings import settings
router = APIRouter(
prefix="/shares",
@ -52,6 +54,52 @@ async def create_share_link(share_in: ShareCreate, current_user: User = Depends(
hashed_password=hashed_password,
permission_level=share_in.permission_level,
)
if share_in.invite_email:
share_url = f"https://{settings.DOMAIN_NAME}/share/{token}"
item_type = "file" if file else "folder"
item_name = file.name if file else folder.name
expiry_text = f" until {share_in.expires_at.strftime('%Y-%m-%d %H:%M')}" if share_in.expires_at else ""
password_text = f"\n\nPassword: {share_in.password}" if share_in.password else ""
email_body = f"""Hello,
{current_user.username} has shared a {item_type} with you: {item_name}
Permission level: {share_in.permission_level}
Access link: {share_url}{password_text}
This link is valid{expiry_text}.
--
RBox File Sharing Service"""
email_html = f"""
<html>
<body>
<p>Hello,</p>
<p><strong>{current_user.username}</strong> has shared a {item_type} with you: <strong>{item_name}</strong></p>
<p><strong>Permission level:</strong> {share_in.permission_level}</p>
<p><a href="{share_url}" style="background-color: #4CAF50; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px; display: inline-block;">Access {item_type.capitalize()}</a></p>
{f'<p><strong>Password:</strong> {share_in.password}</p>' if share_in.password else ''}
<p><small>This link is valid{expiry_text}.</small></p>
<hr>
<p><small>RBox File Sharing Service</small></p>
</body>
</html>
"""
try:
await send_email(
to_email=share_in.invite_email,
subject=f"{current_user.username} shared {item_name} with you",
body=email_body,
html=email_html
)
except Exception as e:
print(f"Failed to send invitation email: {e}")
return await ShareOut.from_tortoise_orm(share)
@router.get("/my", response_model=List[ShareOut])

View File

@ -47,6 +47,7 @@ class ShareCreate(BaseModel):
expires_at: Optional[datetime] = None
password: Optional[str] = None
permission_level: str = "viewer"
invite_email: Optional[EmailStr] = None
class TeamCreate(BaseModel):
name: str

View File

@ -256,7 +256,7 @@ class APIClient {
});
}
async createShare(fileId = null, folderId = null, expiresAt = null, password = null, permissionLevel = 'viewer') {
async createShare(fileId = null, folderId = null, expiresAt = null, password = null, permissionLevel = 'viewer', inviteEmail = null) {
return this.request('shares/', {
method: 'POST',
body: {
@ -264,7 +264,8 @@ class APIClient {
folder_id: folderId,
expires_at: expiresAt,
password,
permission_level: permissionLevel
permission_level: permissionLevel,
invite_email: inviteEmail
}
});
}

View File

@ -113,6 +113,7 @@ export class FileList extends HTMLElement {
` : ''}
<div class="file-grid">
${totalItems === 0 ? '<p class="empty-state">No files found.</p>' : ''}
${this.folders.map(folder => this.renderFolder(folder)).join('')}
${this.files.map(file => this.renderFile(file)).join('')}
</div>

View File

@ -30,7 +30,7 @@ class PhotoGallery extends HTMLElement {
async renderPhotos() {
const grid = this.querySelector('.gallery-grid');
if (this.photos.length === 0) {
grid.innerHTML = '<p class="empty-state">No photos yet</p>';
grid.innerHTML = '<p class="empty-state">No photos found.</p>';
return;
}

View File

@ -20,6 +20,11 @@ export class ShareModal extends HTMLElement {
</div>
<form id="share-form">
<div class="form-group">
<label>Invite Email (optional)</label>
<input type="email" name="invite_email" class="input-field" placeholder="Send invitation to email">
</div>
<div class="form-group">
<label>Permission Level</label>
<select name="permission_level" class="input-field">
@ -101,6 +106,7 @@ export class ShareModal extends HTMLElement {
const errorDiv = this.querySelector('#share-error');
try {
const inviteEmail = formData.get('invite_email') || null;
const permissionLevel = formData.get('permission_level');
const password = formData.get('password') || null;
const expiresAt = formData.get('expires_at') || null;
@ -110,13 +116,20 @@ export class ShareModal extends HTMLElement {
this.folderId,
expiresAt,
password,
permissionLevel
permissionLevel,
inviteEmail
);
const shareLink = `${window.location.origin}/share/${share.token}`;
this.querySelector('#share-link').value = shareLink;
this.querySelector('#share-result').style.display = 'block';
errorDiv.textContent = '';
if (inviteEmail) {
document.dispatchEvent(new CustomEvent('show-toast', {
detail: { message: `Invitation sent to ${inviteEmail}`, type: 'success' }
}));
}
} catch (error) {
errorDiv.textContent = 'Failed to create share link: ' + error.message;
}