initial commit

This commit is contained in:
2025-01-06 11:49:58 +01:00
commit a5b6b54429
23 changed files with 662 additions and 0 deletions

0
wallet_api/__init__.py Normal file
View File

3
wallet_api/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
wallet_api/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class WalletApiConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'wallet_api'

View File

@@ -0,0 +1,37 @@
# Generated by Django 5.1.4 on 2025-01-06 10:24
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='MasterHash',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('hash', models.BinaryField()),
('created_at', models.DateTimeField(auto_now_add=True)),
],
),
migrations.CreateModel(
name='PasswordEntry',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('site', models.CharField(max_length=255)),
('username', models.CharField(max_length=255)),
('password', models.TextField()),
('client_id', models.CharField(max_length=255)),
('topic', models.CharField(max_length=255)),
('created_at', models.DateTimeField(auto_now_add=True)),
],
options={
'unique_together': {('site', 'username', 'client_id')},
},
),
]

View File

18
wallet_api/models.py Normal file
View File

@@ -0,0 +1,18 @@
from django.db import models
# Create your models here.
class PasswordEntry(models.Model):
site = models.CharField(max_length=255)
username = models.CharField(max_length=255)
password = models.TextField()
client_id = models.CharField(max_length=255)
topic = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
unique_together = ('site', 'username', 'client_id')
class MasterHash(models.Model):
hash = models.BinaryField()
created_at = models.DateTimeField(auto_now_add=True)

3
wallet_api/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

9
wallet_api/urls.py Normal file
View File

@@ -0,0 +1,9 @@
from django.urls import path
from . import views
urlpatterns = [
path('add/', views.add_password_api, name='add_password'),
path('get/', views.get_password_api, name='get_password'),
path('delete/', views.delete_password_api, name='delete_password'),
path('list/', views.list_sites_api, name='list_sites'),
]

44
wallet_api/utils.py Normal file
View File

@@ -0,0 +1,44 @@
import bcrypt
import base64
import hashlib
import json
from cryptography.fernet import Fernet
from .models import MasterHash
from django.http import JsonResponse
# Salva l'hash della master password
def save_master_hash(hash):
MasterHash.objects.create(
hash=hash,
)
# Carica l'hash della master password
def load_master_hash():
try:
entry = MasterHash.objects.get(id="1")
return entry.hash
except Exception as e:
return None
# Autenticazione della master password
def authenticate(master_password):
master_hash = load_master_hash()
if master_hash is None:
hashed_password = bcrypt.hashpw(master_password.encode(), bcrypt.gensalt())
save_master_hash(hashed_password)
return True
auth_success = bcrypt.checkpw(master_password.encode(), master_hash.tobytes())
return auth_success
def derive_key(master_password):
hash = hashlib.sha256(master_password.encode()).digest()
return base64.urlsafe_b64encode(hash)
def encrypt_password(password, key):
cipher = Fernet(key)
return cipher.encrypt(password.encode()).decode()
def decrypt_password(encrypted_password, key):
cipher = Fernet(key)
return cipher.decrypt(encrypted_password.encode()).decode()

84
wallet_api/views.py Normal file
View File

@@ -0,0 +1,84 @@
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from .models import PasswordEntry
from .utils import authenticate, derive_key, encrypt_password, decrypt_password
import json
@csrf_exempt
def add_password_api(request):
if request.method == 'POST':
data = json.loads(request.body)
master_password = data.get('master_password')
site = data.get('site')
username = data.get('username')
password = data.get('password')
client_id = data.get('client_id')
topic = data.get('topic')
if not authenticate(master_password):
return JsonResponse({"error": "Master password errata"}, status=403)
key = derive_key(master_password)
encrypted_password = encrypt_password(password, key)
PasswordEntry.objects.create(
site=site,
username=username,
password=encrypted_password,
client_id=client_id,
topic=topic
)
return JsonResponse({"message": "Password aggiunta con successo"})
@csrf_exempt
def get_password_api(request):
if request.method == 'POST':
data = json.loads(request.body)
master_password = data.get('master_password')
site = data.get('site')
if not authenticate(master_password):
return JsonResponse({"error": "Master password errata"}, status=403)
key = derive_key(master_password)
try:
entry = PasswordEntry.objects.get(site=site)
decrypted_password = decrypt_password(entry.password, key)
return JsonResponse({
"site": entry.site,
"username": entry.username,
"password": decrypted_password,
"client_id": entry.client_id,
"topic": entry.topic
})
except PasswordEntry.DoesNotExist:
return JsonResponse({"error": "Sito non trovato"}, status=404)
@csrf_exempt
def delete_password_api(request):
if request.method == 'POST':
data = json.loads(request.body)
master_password = data.get('master_password')
site = data.get('site')
if not authenticate(master_password):
return JsonResponse({"error": "Master password errata"}, status=403)
try:
entry = PasswordEntry.objects.get(site=site)
entry.delete()
return JsonResponse({"message": "Password cancellata con successo"})
except PasswordEntry.DoesNotExist:
return JsonResponse({"error": "Sito non trovato"}, status=404)
@csrf_exempt
def list_sites_api(request):
if request.method == 'POST':
data = json.loads(request.body)
master_password = data.get('master_password')
if not authenticate(master_password):
return JsonResponse({"error": "Master password errata"}, status=403)
sites = PasswordEntry.objects.values_list('site', flat=True)
return JsonResponse({"sites": list(sites)})