initial commit
This commit is contained in:
0
wallet_api/__init__.py
Normal file
0
wallet_api/__init__.py
Normal file
3
wallet_api/admin.py
Normal file
3
wallet_api/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
wallet_api/apps.py
Normal file
6
wallet_api/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class WalletApiConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'wallet_api'
|
||||
37
wallet_api/migrations/0001_initial.py
Normal file
37
wallet_api/migrations/0001_initial.py
Normal 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')},
|
||||
},
|
||||
),
|
||||
]
|
||||
0
wallet_api/migrations/__init__.py
Normal file
0
wallet_api/migrations/__init__.py
Normal file
18
wallet_api/models.py
Normal file
18
wallet_api/models.py
Normal 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
3
wallet_api/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
9
wallet_api/urls.py
Normal file
9
wallet_api/urls.py
Normal 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
44
wallet_api/utils.py
Normal 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
84
wallet_api/views.py
Normal 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)})
|
||||
Reference in New Issue
Block a user