Compare commits

..

No commits in common. "d98e8e2d48597333e06bf781ac27588fd792f0ec" and "d04216ad795f0c8d84c542b7787621c1dab54fa0" have entirely different histories.

17 changed files with 47 additions and 233 deletions

View File

@ -1,20 +1,2 @@
DOMAIN=localhost DOMAIN=localhost
DEMO=false DEMO=false
STATIC_ROOT=/tmp/static/
MEDIA_ROOT=/tmp/media/
ALLOWED_HOSTS=localhost,localhost:8000,127.0.0.1,
DOMAIN=localhost
DEBUG=True
EMAIL_HOST="mail.example.org"
EMAIL_HOST_USER="fillme_noreply"
EMAIL_HOST_PASSWORD="fillme_passwd"
EMAIL_PORT=587
EMAIL_USE_TLS=True
EMAIL_BACKEND="django.core.mail.backends.smtp.EmailBackend"
EMAIL_FILE_PATH="/tmp/app-messages"
ENABLE_EMAIL=false
PREDEFINED_TOKEN='5018dd65-9abd-4a62-8896-80f34ac66150'
# TODO review these vars
#SNAPSHOTS_DIR=/path/to/TODO
#EVIDENCES_DIR=/path/to/TODO

View File

@ -1,27 +1,32 @@
import json import json
from uuid import uuid4
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.http import JsonResponse
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.core.exceptions import ValidationError
from django_tables2 import SingleTableView from django_tables2 import SingleTableView
from django.views.generic.base import View
from django.views.generic.edit import ( from django.views.generic.edit import (
CreateView, CreateView,
DeleteView, DeleteView,
UpdateView, UpdateView,
) )
from django.http import JsonResponse
from uuid import uuid4
from utils.save_snapshots import move_json, save_in_disk
from dashboard.mixins import DashboardView from dashboard.mixins import DashboardView
from evidence.models import Annotation from evidence.models import Annotation
from evidence.parse import Build from evidence.parse import Build
from user.models import User
from api.models import Token from api.models import Token
from api.tables import TokensTable from api.tables import TokensTable
def save_in_disk(data, user):
pass
@csrf_exempt @csrf_exempt
def NewSnapshot(request): def NewSnapshot(request):
# Accept only posts # Accept only posts
@ -54,16 +59,15 @@ def NewSnapshot(request):
).first() ).first()
if exist_annotation: if exist_annotation:
txt = "error: the snapshot {} exist".format(data['uuid']) raise ValidationError("error: the snapshot {} exist".format(data['uuid']))
return JsonResponse({'status': txt}, status=500)
# Process snapshot # Process snapshot
path_name = save_in_disk(data, tk.owner.institution.name) # save_in_disk(data, tk.user)
try: try:
Build(data, tk.owner) Build(data, tk.owner)
except Exception as err: except Exception:
return JsonResponse({'status': f"fail: {err}"}, status=500) return JsonResponse({'status': 'fail'}, status=200)
annotation = Annotation.objects.filter( annotation = Annotation.objects.filter(
uuid=data['uuid'], uuid=data['uuid'],
@ -75,7 +79,7 @@ def NewSnapshot(request):
if not annotation: if not annotation:
return JsonResponse({'status': 'fail'}, status=500) return JsonResponse({'status': 'fail'}, status=200)
url_args = reverse_lazy("device:details", args=(annotation.value,)) url_args = reverse_lazy("device:details", args=(annotation.value,))
url = request.build_absolute_uri(url_args) url = request.build_absolute_uri(url_args)
@ -87,8 +91,6 @@ def NewSnapshot(request):
# TODO replace with public_url when available # TODO replace with public_url when available
"public_url": url "public_url": url
} }
move_json(path_name, tk.owner.institution.name)
return JsonResponse(response, status=200) return JsonResponse(response, status=200)

View File

@ -1,5 +1,4 @@
from django.urls import resolve from django.urls import resolve
from django.conf import settings
from django.shortcuts import get_object_or_404, redirect, Http404 from django.shortcuts import get_object_or_404, redirect, Http404
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
@ -33,7 +32,6 @@ class DashboardView(LoginRequiredMixin):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context.update({ context.update({
"commit_id": settings.COMMIT,
'title': self.title, 'title': self.title,
'subtitle': self.subtitle, 'subtitle': self.subtitle,
'breadcrumb': self.breadcrumb, 'breadcrumb': self.breadcrumb,

View File

@ -69,17 +69,23 @@ class SearchView(InventaryMixin):
if not matches.size(): if not matches.size():
return self.search_hids(query, offset, limit) return self.search_hids(query, offset, limit)
devices = [] annotations = []
for x in matches: for x in matches:
devices.append(self.get_annotations(x)) annotations.extend(self.get_annotations(x))
devices = [Device(id=x) for x in set(annotations)]
count = matches.size() count = matches.size()
return devices, count return devices, count
def get_annotations(self, xp): def get_annotations(self, xp):
snap = xp.document.get_data() snap = xp.document.get_data()
uuid = json.loads(snap).get('uuid') uuid = json.loads(snap).get('uuid')
return Device.get_annotation_from_uuid(uuid, self.request.user.institution)
return Annotation.objects.filter(
type=Annotation.Type.SYSTEM,
owner=self.request.user.institution,
uuid=uuid
).values_list("value", flat=True).distinct()
def search_hids(self, query, offset, limit): def search_hids(self, query, offset, limit):
qry = Q() qry = Q()

View File

@ -1,6 +1,5 @@
from django import forms from django import forms
from utils.device import create_annotation, create_doc, create_index from utils.device import create_annotation, create_doc, create_index
from utils.save_snapshots import move_json, save_in_disk
DEVICE_TYPES = [ DEVICE_TYPES = [
@ -57,11 +56,8 @@ class BaseDeviceFormSet(forms.BaseFormSet):
if not commit: if not commit:
return doc return doc
path_name = save_in_disk(doc, self.user.institution.name, place="placeholder")
create_index(doc, self.user) create_index(doc, self.user)
create_annotation(doc, user, commit=commit) create_annotation(doc, user, commit=commit)
move_json(path_name, self.user.institution.name, place="placeholder")
return doc return doc

View File

@ -90,11 +90,9 @@ class Device:
def get_hids(self): def get_hids(self):
annotations = self.get_annotations() annotations = self.get_annotations()
algos = list(ALGOS.keys())
algos.append('CUSTOM_ID')
self.hids = list(set(annotations.filter( self.hids = list(set(annotations.filter(
type=Annotation.Type.SYSTEM, type=Annotation.Type.SYSTEM,
key__in=algos, key__in=ALGOS.keys(),
).values_list("value", flat=True))) ).values_list("value", flat=True)))
def get_evidences(self): def get_evidences(self):
@ -120,32 +118,9 @@ class Device:
def get_unassigned(cls, institution, offset=0, limit=None): def get_unassigned(cls, institution, offset=0, limit=None):
sql = """ sql = """
WITH RankedAnnotations AS ( SELECT DISTINCT t1.value from evidence_annotation as t1
SELECT left join lot_devicelot as t2 on t1.value = t2.device_id
t1.value, where t2.device_id is null and owner_id=={institution} and type=={type}
t1.key,
ROW_NUMBER() OVER (
PARTITION BY t1.uuid
ORDER BY
CASE
WHEN t1.key = 'CUSTOM_ID' THEN 1
WHEN t1.key = 'hidalgo1' THEN 2
ELSE 3
END,
t1.created DESC
) AS row_num
FROM evidence_annotation AS t1
LEFT JOIN lot_devicelot AS t2 ON t1.value = t2.device_id
WHERE t2.device_id IS NULL
AND t1.owner_id = {institution}
AND t1.type = {type}
)
SELECT DISTINCT
value
FROM
RankedAnnotations
WHERE
row_num = 1
""".format( """.format(
institution=institution.id, institution=institution.id,
type=Annotation.Type.SYSTEM, type=Annotation.Type.SYSTEM,
@ -169,83 +144,18 @@ class Device:
def get_unassigned_count(cls, institution): def get_unassigned_count(cls, institution):
sql = """ sql = """
WITH RankedAnnotations AS ( SELECT count(DISTINCT t1.value) from evidence_annotation as t1
SELECT left join lot_devicelot as t2 on t1.value = t2.device_id
t1.value, where t2.device_id is null and owner_id=={institution} and type=={type};
t1.key,
ROW_NUMBER() OVER (
PARTITION BY t1.uuid
ORDER BY
CASE
WHEN t1.key = 'CUSTOM_ID' THEN 1
WHEN t1.key = 'hidalgo1' THEN 2
ELSE 3
END,
t1.created DESC
) AS row_num
FROM evidence_annotation AS t1
LEFT JOIN lot_devicelot AS t2 ON t1.value = t2.device_id
WHERE t2.device_id IS NULL
AND t1.owner_id = {institution}
AND t1.type = {type}
)
SELECT
COUNT(DISTINCT value)
FROM
RankedAnnotations
WHERE
row_num = 1
""".format( """.format(
institution=institution.id, institution=institution.id,
type=Annotation.Type.SYSTEM, type=Annotation.Type.SYSTEM,
) )
with connection.cursor() as cursor: with connection.cursor() as cursor:
cursor.execute(sql) cursor.execute(sql)
return cursor.fetchall()[0][0] return cursor.fetchall()[0][0]
@classmethod
def get_annotation_from_uuid(cls, uuid, institution):
sql = """
WITH RankedAnnotations AS (
SELECT
t1.value,
t1.key,
ROW_NUMBER() OVER (
PARTITION BY t1.uuid
ORDER BY
CASE
WHEN t1.key = 'CUSTOM_ID' THEN 1
WHEN t1.key = 'hidalgo1' THEN 2
ELSE 3
END,
t1.created DESC
) AS row_num
FROM evidence_annotation AS t1
LEFT JOIN lot_devicelot AS t2 ON t1.value = t2.device_id
WHERE t2.device_id IS NULL
AND t1.owner_id = {institution}
AND t1.type = {type}
AND t1.uuid = '{uuid}'
)
SELECT DISTINCT
value
FROM
RankedAnnotations
WHERE
row_num = 1;
""".format(
uuid=uuid.replace("-", ""),
institution=institution.id,
type=Annotation.Type.SYSTEM,
)
annotations = []
with connection.cursor() as cursor:
cursor.execute(sql)
annotations = cursor.fetchall()
return cls(id=annotations[0][0])
@property @property
def is_websnapshot(self): def is_websnapshot(self):
if not self.last_evidence: if not self.last_evidence:

View File

@ -10,7 +10,6 @@ For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.0/ref/settings/ https://docs.djangoproject.com/en/5.0/ref/settings/
""" """
import os
import xapian import xapian
from pathlib import Path from pathlib import Path
@ -36,7 +35,7 @@ assert DOMAIN not in [None, ''], "DOMAIN var is MANDATORY"
print("DOMAIN: " + DOMAIN) print("DOMAIN: " + DOMAIN)
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default=DOMAIN, cast=Csv()) ALLOWED_HOSTS = config('ALLOWED_HOSTS', default=DOMAIN, cast=Csv())
assert DOMAIN in ALLOWED_HOSTS, f"DOMAIN {DOMAIN} is not in ALLOWED_HOSTS {ALLOWED_HOSTS}" assert DOMAIN in ALLOWED_HOSTS, "DOMAIN is not ALLOWED_HOST"
CSRF_TRUSTED_ORIGINS = config('CSRF_TRUSTED_ORIGINS', default=f'https://{DOMAIN}', cast=Csv()) CSRF_TRUSTED_ORIGINS = config('CSRF_TRUSTED_ORIGINS', default=f'https://{DOMAIN}', cast=Csv())
@ -63,7 +62,6 @@ EMAIL_FILE_PATH = config('EMAIL_FILE_PATH', default='/tmp/app-messages')
ENABLE_EMAIL = config("ENABLE_EMAIL", default=True, cast=bool) ENABLE_EMAIL = config("ENABLE_EMAIL", default=True, cast=bool)
EVIDENCES_DIR = config("EVIDENCES_DIR", default=os.path.join(BASE_DIR, "db"))
# Application definition # Application definition
@ -164,11 +162,6 @@ USE_I18N = True
USE_TZ = True USE_TZ = True
USE_L10N = True
LANGUAGES = [
('es', 'Spanish'),
('en', 'English'),
]
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.0/howto/static-files/ # https://docs.djangoproject.com/en/5.0/howto/static-files/
@ -213,4 +206,3 @@ LOGGING = {
SNAPSHOT_PATH="/tmp/" SNAPSHOT_PATH="/tmp/"
DATA_UPLOAD_MAX_NUMBER_FILES = 1000 DATA_UPLOAD_MAX_NUMBER_FILES = 1000
COMMIT = config('COMMIT', default='')

View File

@ -6,9 +6,7 @@ services:
environment: environment:
- DEBUG=true - DEBUG=true
- DOMAIN=${DOMAIN:-localhost} - DOMAIN=${DOMAIN:-localhost}
- ALLOWED_HOSTS=${ALLOWED_HOSTS:-$DOMAIN} - DEMO=${DEMO:-n}
- DEMO=${DEMO:-false}
- PREDEFINED_TOKEN=${PREDEFINED_TOKEN:-}
volumes: volumes:
- .:/opt/devicehub-django - .:/opt/devicehub-django
ports: ports:

View File

@ -9,13 +9,11 @@ set -u
set -x set -x
main() { main() {
cd "$(dirname "${0}")"
if [ "${DETACH:-}" ]; then if [ "${DETACH:-}" ]; then
detach_arg='-d' detach_arg='-d'
fi fi
# remove old database # remove old database
sudo rm -vfr ./db/* sudo rm -vf db/*
docker compose down -v docker compose down -v
docker compose build docker compose build
docker compose up ${detach_arg:-} docker compose up ${detach_arg:-}

View File

@ -12,14 +12,6 @@ check_app_is_there() {
} }
deploy() { deploy() {
# TODO this is weird, find better workaround
git config --global --add safe.directory /opt/devicehub-django
export COMMIT=$(git log --format="%H %ad" --date=iso -n 1)
if [ "${DEBUG:-}" = 'true' ]; then
./manage.py print_settings
fi
# detect if existing deployment (TODO only works with sqlite) # detect if existing deployment (TODO only works with sqlite)
if [ -f "${program_dir}/db/db.sqlite3" ]; then if [ -f "${program_dir}/db/db.sqlite3" ]; then
echo "INFO: detected EXISTING deployment" echo "INFO: detected EXISTING deployment"
@ -32,13 +24,11 @@ deploy() {
INIT_ORG="${INIT_ORG:-example-org}" INIT_ORG="${INIT_ORG:-example-org}"
INIT_USER="${INIT_USER:-user@example.org}" INIT_USER="${INIT_USER:-user@example.org}"
INIT_PASSWD="${INIT_PASSWD:-1234}" INIT_PASSWD="${INIT_PASSWD:-1234}"
ADMIN='True'
PREDEFINED_TOKEN="${PREDEFINED_TOKEN:-}"
./manage.py add_institution "${INIT_ORG}" ./manage.py add_institution "${INIT_ORG}"
# TODO: one error on add_user, and you don't add user anymore # TODO: one error on add_user, and you don't add user anymore
./manage.py add_user "${INIT_ORG}" "${INIT_USER}" "${INIT_PASSWD}" "${ADMIN}" "${PREDEFINED_TOKEN}" ./manage.py add_user "${INIT_ORG}" "${INIT_USER}" "${INIT_PASSWD}"
if [ "${DEMO:-}" = 'true' ]; then if [ "${DEMO:-}" ]; then
./manage.py up_snapshots example/snapshots/ "${INIT_USER}" ./manage.py up_snapshots example/snapshots/ "${INIT_USER}"
fi fi
fi fi
@ -46,7 +36,7 @@ deploy() {
runserver() { runserver() {
PORT="${PORT:-8000}" PORT="${PORT:-8000}"
if [ "${DEBUG:-}" = 'true' ]; then if [ "${DEBUG:-}" ]; then
./manage.py runserver 0.0.0.0:${PORT} ./manage.py runserver 0.0.0.0:${PORT}
else else
# TODO # TODO

View File

@ -9,7 +9,6 @@ from utils.forms import MultipleFileField
from device.models import Device from device.models import Device
from evidence.parse import Build from evidence.parse import Build
from evidence.models import Annotation from evidence.models import Annotation
from utils.save_snapshots import move_json, save_in_disk
class UploadForm(forms.Form): class UploadForm(forms.Form):
@ -49,9 +48,7 @@ class UploadForm(forms.Form):
return return
for ev in self.evidences: for ev in self.evidences:
path_name = save_in_disk(ev[1], user.institution.name)
Build(ev[1], user) Build(ev[1], user)
move_json(path_name, user.institution.name)
class UserTagForm(forms.Form): class UserTagForm(forms.Form):
@ -154,11 +151,8 @@ class ImportForm(forms.Form):
if commit: if commit:
for doc, cred in table: for doc, cred in table:
path_name = save_in_disk(doc, self.user.institution.name, place="placeholder")
cred.save() cred.save()
create_index(doc, self.user) create_index(doc, self.user)
move_json(path_name, self.user.institution.name, place="placeholder")
return table return table
return return

View File

@ -126,7 +126,6 @@ class Evidence:
return Annotation.objects.filter( return Annotation.objects.filter(
owner=user.institution, owner=user.institution,
type=Annotation.Type.SYSTEM, type=Annotation.Type.SYSTEM,
key="hidalgo1",
).order_by("-created").values_list("uuid", flat=True).distinct() ).order_by("-created").values_list("uuid", flat=True).distinct()
def set_components(self): def set_components(self):

View File

@ -113,8 +113,7 @@ class Build:
# mac = get_mac2(hwinfo_raw) or "" # mac = get_mac2(hwinfo_raw) or ""
mac = get_mac(lshw) or "" mac = get_mac(lshw) or ""
if not mac: if not mac:
print(f"WARNING: Could not retrieve MAC address in snapshot {snapshot['uuid']}" ) print("WARNING!! No there are MAC address")
# TODO generate system annotation for that snapshot
else: else:
print(f"{manufacturer}{model}{chassis}{serial_number}{sku}{mac}") print(f"{manufacturer}{model}{chassis}{serial_number}{sku}{mac}")

View File

@ -42,5 +42,4 @@
<div id="login-footer" class="mt-3"> <div id="login-footer" class="mt-3">
<a href="{% url 'login:password_reset' %}" data-toggle="modal" data-target="#forgotPasswordModal">{% trans "Forgot your password? Click here to recover" %}</a> <a href="{% url 'login:password_reset' %}" data-toggle="modal" data-target="#forgotPasswordModal">{% trans "Forgot your password? Click here to recover" %}</a>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,6 +1,5 @@
import logging import logging
from django.conf import settings
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.contrib.auth import views as auth_views from django.contrib.auth import views as auth_views
from django.contrib.auth import login as auth_login from django.contrib.auth import login as auth_login
@ -18,7 +17,7 @@ class LoginView(auth_views.LoginView):
extra_context = { extra_context = {
'title': _('Login'), 'title': _('Login'),
'success_url': reverse_lazy('dashboard:unassigned_devices'), 'success_url': reverse_lazy('dashboard:unassigned_devices'),
'commit_id': settings.COMMIT, # 'commit_id': settings.COMMIT,
} }
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
@ -66,6 +65,7 @@ class PasswordResetView(auth_views.PasswordResetView):
success_url = reverse_lazy('login:password_reset_done') success_url = reverse_lazy('login:password_reset_done')
def form_valid(self, form): def form_valid(self, form):
import pdb; pdb.set_trace()
try: try:
response = super().form_valid(form) response = super().form_valid(form)
return response return response

View File

@ -17,17 +17,15 @@ class Command(BaseCommand):
parser.add_argument('email', type=str, help='email') parser.add_argument('email', type=str, help='email')
parser.add_argument('password', type=str, help='password') parser.add_argument('password', type=str, help='password')
parser.add_argument('is_admin', nargs='?', default=False, type=str, help='is admin') parser.add_argument('is_admin', nargs='?', default=False, type=str, help='is admin')
parser.add_argument('predefined_token', nargs='?', default='', type=str, help='predefined token')
def handle(self, *args, **kwargs): def handle(self, *args, **kwargs):
email = kwargs['email'] email = kwargs['email']
password = kwargs['password'] password = kwargs['password']
is_admin = kwargs['is_admin'] is_admin = kwargs['is_admin']
predefined_token = kwargs['predefined_token']
institution = Institution.objects.get(name=kwargs['institution']) institution = Institution.objects.get(name=kwargs['institution'])
self.create_user(institution, email, password, is_admin, predefined_token) self.create_user(institution, email, password, is_admin)
def create_user(self, institution, email, password, is_admin, predefined_token): def create_user(self, institution, email, password, is_admin):
self.u = User.objects.create( self.u = User.objects.create(
institution=institution, institution=institution,
email=email, email=email,
@ -36,10 +34,6 @@ class Command(BaseCommand):
) )
self.u.set_password(password) self.u.set_password(password)
self.u.save() self.u.save()
if predefined_token:
token = predefined_token
else:
token = uuid4() token = uuid4()
Token.objects.create(token=token, owner=self.u) Token.objects.create(token=token, owner=self.u)
print(f"TOKEN: {token}") print(f"TOKEN: {token}")

View File

@ -1,43 +0,0 @@
import os
import json
import shutil
from datetime import datetime
from django.conf import settings
def move_json(path_name, user, place="snapshots"):
if place != "snapshots":
place = "placeholders"
tmp_snapshots = settings.EVIDENCES_DIR
path_dir = os.path.join(tmp_snapshots, user, place)
if os.path.isfile(path_name):
shutil.copy(path_name, path_dir)
os.remove(path_name)
def save_in_disk(data, user, place="snapshots"):
uuid = data.get('uuid', '')
now = datetime.now()
year = now.year
month = now.month
day = now.day
hour = now.hour
minutes = now.minute
tmp_snapshots = settings.EVIDENCES_DIR
if place != "snapshots":
place = "placeholders"
name_file = f"{year}-{month}-{day}-{hour}-{minutes}_{uuid}.json"
path_dir = os.path.join(tmp_snapshots, user, place, "errors")
path_name = os.path.join(path_dir, name_file)
if not os.path.isdir(path_dir):
os.system(f'mkdir -p {path_dir}')
with open(path_name, 'w') as snapshot_file:
snapshot_file.write(json.dumps(data))
return path_name