hours #14

Merged
cayop merged 6 commits from hours into main 2024-10-21 16:51:21 +00:00
25 changed files with 422 additions and 59 deletions
Showing only changes of commit a0548b38c7 - Show all commits

View File

@ -1,2 +1,20 @@
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

69
admin/email.py Normal file
View File

@ -0,0 +1,69 @@
import logging
from django.conf import settings
from django.template import loader
from django.core.mail import EmailMultiAlternatives
from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.shortcuts import get_current_site
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
logger = logging.getLogger(__name__)
class NotifyActivateUserByEmail:
subject_template_name = 'activate_user_subject.txt'
email_template_name = 'activate_user_email.txt'
html_email_template_name = 'activate_user_email.html'
def get_email_context(self, user, token):
"""
Define a new context with a token for put in a email
when send a email for add a new password
"""
protocol = 'https' if self.request.is_secure() else 'http'
current_site = get_current_site(self.request)
site_name = current_site.name
domain = current_site.domain
if not token:
token = default_token_generator.make_token(user)
context = {
'email': user.email,
'domain': domain,
'site_name': site_name,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'user': user,
'token': token,
'protocol': protocol,
}
return context
def send_email(self, user, token=None):
"""
Send a email when a user is activated.
"""
context = self.get_email_context(user, token)
subject = loader.render_to_string(self.subject_template_name, context)
# Email subject *must not* contain newlines
subject = ''.join(subject.splitlines())
body = loader.render_to_string(self.email_template_name, context)
from_email = settings.DEFAULT_FROM_EMAIL
to_email = user.email
email_message = EmailMultiAlternatives(
subject, body, from_email, [to_email])
html_email = loader.render_to_string(self.html_email_template_name, context)
email_message.attach_alternative(html_email, 'text/html')
try:
if settings.ENABLE_EMAIL:
email_message.send()
return
logger.warning(to_email)
logger.warning(body)
except Exception as err:
logger.error(err)
return

View File

@ -1,3 +1,4 @@
from smtplib import SMTPException
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -9,6 +10,7 @@ from django.views.generic.edit import (
) )
from dashboard.mixins import DashboardView, Http403 from dashboard.mixins import DashboardView, Http403
from user.models import User, Institution from user.models import User, Institution
from admin.email import NotifyActivateUserByEmail
class AdminView(DashboardView): class AdminView(DashboardView):
@ -42,7 +44,7 @@ class UsersView(AdminView, TemplateView):
return context return context
class CreateUserView(AdminView, CreateView): class CreateUserView(AdminView, NotifyActivateUserByEmail, CreateView):
template_name = "user.html" template_name = "user.html"
title = _("User") title = _("User")
breadcrumb = _("admin / User") + " /" breadcrumb = _("admin / User") + " /"
@ -58,6 +60,12 @@ class CreateUserView(AdminView, CreateView):
form.instance.institution = self.request.user.institution form.instance.institution = self.request.user.institution
form.instance.set_password(form.instance.password) form.instance.set_password(form.instance.password)
response = super().form_valid(form) response = super().form_valid(form)
try:
self.send_email(form.instance)
except SMTPException as e:
messages.error(self.request, e)
return response return response

View File

@ -1,32 +1,27 @@
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
@ -59,19 +54,42 @@ def NewSnapshot(request):
).first() ).first()
if exist_annotation: if exist_annotation:
raise ValidationError("error: the snapshot {} exist".format(data['uuid'])) txt = "error: the snapshot {} exist".format(data['uuid'])
return JsonResponse({'status': txt}, status=500)
# Process snapshot # Process snapshot
# save_in_disk(data, tk.user) path_name = save_in_disk(data, tk.owner.institution.name)
try: try:
Build(data, tk.owner) Build(data, tk.owner)
except Exception: except Exception as err:
return JsonResponse({'status': 'fail'}, status=200) return JsonResponse({'status': f"fail: {err}"}, status=500)
return JsonResponse({'status': 'success'}, status=200) annotation = Annotation.objects.filter(
uuid=data['uuid'],
type=Annotation.Type.SYSTEM,
# TODO this is hardcoded, it should select the user preferred algorithm
key="hidalgo1",
owner=tk.owner.institution
).first()
if not annotation:
return JsonResponse({'status': 'fail'}, status=500)
url_args = reverse_lazy("device:details", args=(annotation.value,))
url = request.build_absolute_uri(url_args)
response = {
"status": "success",
"dhid": annotation.value[:6].upper(),
"url": url,
# TODO replace with public_url when available
"public_url": url
}
move_json(path_name, tk.owner.institution.name)
return JsonResponse(response, status=200)
class TokenView(DashboardView, SingleTableView): class TokenView(DashboardView, SingleTableView):

View File

@ -1,4 +1,5 @@
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
@ -32,6 +33,7 @@ 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

@ -15,7 +15,7 @@
{% trans 'Documents' %} {% trans 'Documents' %}
</a> </a>
{% endif %} {% endif %}
<a href="{# url 'idhub:admin_people_activate' object.id #}" type="button" class="btn btn-green-admin"> <a href="{# url 'dashboard:exports' object.id #}" type="button" class="btn btn-green-admin">
<i class="bi bi-reply"></i> <i class="bi bi-reply"></i>
{% trans 'Exports' %} {% trans 'Exports' %}
</a> </a>

View File

@ -69,23 +69,17 @@ 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)
annotations = [] devices = []
for x in matches: for x in matches:
annotations.extend(self.get_annotations(x)) devices.append(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,5 +1,6 @@
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 = [
@ -56,8 +57,11 @@ 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

@ -27,7 +27,7 @@ class Device:
# the id is the chid of the device # the id is the chid of the device
self.id = kwargs["id"] self.id = kwargs["id"]
self.pk = self.id self.pk = self.id
self.shortid = self.pk[:6] self.shortid = self.pk[:6].upper()
self.algorithm = None self.algorithm = None
self.owner = None self.owner = None
self.annotations = [] self.annotations = []
@ -90,9 +90,11 @@ 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.keys(), key__in=algos,
).values_list("value", flat=True))) ).values_list("value", flat=True)))
def get_evidences(self): def get_evidences(self):
@ -118,9 +120,32 @@ class Device:
def get_unassigned(cls, institution, offset=0, limit=None): def get_unassigned(cls, institution, offset=0, limit=None):
sql = """ sql = """
SELECT DISTINCT t1.value from evidence_annotation as t1 WITH RankedAnnotations AS (
left join lot_devicelot as t2 on t1.value = t2.device_id SELECT
where t2.device_id is null and owner_id=={institution} and type=={type} 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}
)
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,
@ -144,18 +169,83 @@ class Device:
def get_unassigned_count(cls, institution): def get_unassigned_count(cls, institution):
sql = """ sql = """
SELECT count(DISTINCT t1.value) from evidence_annotation as t1 WITH RankedAnnotations AS (
left join lot_devicelot as t2 on t1.value = t2.device_id SELECT
where t2.device_id is null and owner_id=={institution} and type=={type}; 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}
)
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,6 +10,7 @@ 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
@ -35,10 +36,35 @@ 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, "DOMAIN is not ALLOWED_HOST" assert DOMAIN in ALLOWED_HOSTS, f"DOMAIN {DOMAIN} is not in ALLOWED_HOSTS {ALLOWED_HOSTS}"
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())
INITIAL_ADMIN_EMAIL = config("INITIAL_ADMIN_EMAIL", default='admin@example.org')
INITIAL_ADMIN_PASSWORD = config("INITIAL_ADMIN_PASSWORD", default='1234')
DEFAULT_FROM_EMAIL = config(
'DEFAULT_FROM_EMAIL', default='webmaster@localhost')
EMAIL_HOST = config('EMAIL_HOST', default='localhost')
EMAIL_HOST_USER = config('EMAIL_HOST_USER', default='')
EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD', default='')
EMAIL_PORT = config('EMAIL_PORT', default=25, cast=int)
EMAIL_USE_TLS = config('EMAIL_USE_TLS', default=False, cast=bool)
EMAIL_BACKEND = config('EMAIL_BACKEND', default='django.core.mail.backends.smtp.EmailBackend')
EMAIL_FILE_PATH = config('EMAIL_FILE_PATH', default='/tmp/app-messages')
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
INSTALLED_APPS = [ INSTALLED_APPS = [
@ -141,6 +167,11 @@ USE_TZ = False
if TIME_ZONE == "UTC": if TIME_ZONE == "UTC":
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/
@ -185,3 +216,4 @@ 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,7 +6,9 @@ services:
environment: environment:
- DEBUG=true - DEBUG=true
- DOMAIN=${DOMAIN:-localhost} - DOMAIN=${DOMAIN:-localhost}
- DEMO=${DEMO:-n} - ALLOWED_HOSTS=${ALLOWED_HOSTS:-$DOMAIN}
- DEMO=${DEMO:-false}
- PREDEFINED_TOKEN=${PREDEFINED_TOKEN:-}
volumes: volumes:
- .:/opt/devicehub-django - .:/opt/devicehub-django
ports: ports:

View File

@ -9,11 +9,13 @@ 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 -vf db/* sudo rm -vfr ./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,6 +12,14 @@ 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"
@ -24,11 +32,13 @@ 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}" ./manage.py add_user "${INIT_ORG}" "${INIT_USER}" "${INIT_PASSWD}" "${ADMIN}" "${PREDEFINED_TOKEN}"
if [ "${DEMO:-}" ]; then if [ "${DEMO:-}" = 'true' ]; then
./manage.py up_snapshots example/snapshots/ "${INIT_USER}" ./manage.py up_snapshots example/snapshots/ "${INIT_USER}"
fi fi
fi fi
@ -36,7 +46,7 @@ deploy() {
runserver() { runserver() {
PORT="${PORT:-8000}" PORT="${PORT:-8000}"
if [ "${DEBUG:-}" ]; then if [ "${DEBUG:-}" = 'true' ]; then
./manage.py runserver 0.0.0.0:${PORT} ./manage.py runserver 0.0.0.0:${PORT}
else else
# TODO # TODO

View File

@ -9,6 +9,7 @@ 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):
@ -48,7 +49,9 @@ 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):
@ -151,8 +154,11 @@ class ImportForm(forms.Form):
if commit: if commit:
for doc, cred in table: for doc, cred in table:
cred.save() path_name = save_in_disk(doc, self.user.institution.name, place="placeholder")
create_index(doc, self.user)
return table cred.save()
create_index(doc, self.user)
move_json(path_name, self.user.institution.name, place="placeholder")
return table
return return

View File

@ -67,7 +67,7 @@ class Evidence:
for xa in matches: for xa in matches:
self.doc = json.loads(xa.document.get_data()) self.doc = json.loads(xa.document.get_data())
if self.doc.get("software") == "EreuseWorkbench": if self.doc.get("software") == "workbench-script":
dmidecode_raw = self.doc["data"]["dmidecode"] dmidecode_raw = self.doc["data"]["dmidecode"]
self.dmi = DMIParse(dmidecode_raw) self.dmi = DMIParse(dmidecode_raw)
@ -80,7 +80,7 @@ class Evidence:
self.created = self.annotations.last().created self.created = self.annotations.last().created
def get_components(self): def get_components(self):
if self.doc.get("software") != "EreuseWorkbench": if self.doc.get("software") != "workbench-script":
return self.doc.get('components', []) return self.doc.get('components', [])
self.set_components() self.set_components()
return self.components return self.components
@ -92,7 +92,7 @@ class Evidence:
return "" return ""
return list(self.doc.get('kv').values())[0] return list(self.doc.get('kv').values())[0]
if self.doc.get("software") != "EreuseWorkbench": if self.doc.get("software") != "workbench-script":
return self.doc['device']['manufacturer'] return self.doc['device']['manufacturer']
return self.dmi.manufacturer().strip() return self.dmi.manufacturer().strip()
@ -104,13 +104,13 @@ class Evidence:
return "" return ""
return list(self.doc.get('kv').values())[1] return list(self.doc.get('kv').values())[1]
if self.doc.get("software") != "EreuseWorkbench": if self.doc.get("software") != "workbench-script":
return self.doc['device']['model'] return self.doc['device']['model']
return self.dmi.model().strip() return self.dmi.model().strip()
def get_chassis(self): def get_chassis(self):
if self.doc.get("software") != "EreuseWorkbench": if self.doc.get("software") != "workbench-script":
return self.doc['device']['model'] return self.doc['device']['model']
chassis = self.dmi.get("Chassis")[0].get("Type", '_virtual') chassis = self.dmi.get("Chassis")[0].get("Type", '_virtual')
@ -126,6 +126,7 @@ 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", "created").distinct() ).order_by("-created").values_list("uuid", "created").distinct()
def set_components(self): def set_components(self):

View File

@ -64,7 +64,7 @@ class Build:
} }
def get_hid_14(self): def get_hid_14(self):
if self.json.get("software") == "EreuseWorkbench": if self.json.get("software") == "workbench-script":
hid = self.get_hid(self.json) hid = self.get_hid(self.json)
else: else:
device = self.json['device'] device = self.json['device']
@ -120,7 +120,8 @@ 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("WARNING!! No there are MAC address") print(f"WARNING: Could not retrieve MAC address in snapshot {snapshot['uuid']}" )
# 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

@ -0,0 +1,31 @@
{% load i18n %}{% autoescape off %}
{% trans "DeviceHub" as site %}
<p>
{% blocktrans %}You're receiving this email because your user account at {{site}} has been activated.{% endblocktrans %}
</p>
<p>
{% trans "Your username is:" %} {{ user.username }}
</p>
<p>
{% trans "Please go to the following page and choose a password:" %}
</p>
<p>
{% block reset_link %}
<a href="{{ protocol }}://{{ domain }}{% url 'login:password_reset_confirm' uidb64=uid token=token %}">
{{ protocol }}://{{ domain }}{% url 'login:password_reset_confirm' uidb64=uid token=token %}
</a>
{% endblock %}
</p>
<p>
{% trans "Thanks for using our site!" %}
</p>
<p>
{% blocktrans %}The {{site}} team{% endblocktrans %}
</p>
{% endautoescape %}

View File

@ -0,0 +1,19 @@
{% load i18n %}{% autoescape off %}
{% trans "DeviceHub" as site %}
{% blocktrans %}You're receiving this email because your user account at {{site}} has been activated.{% endblocktrans %}
{% trans "Your username is:" %} {{ user.username }}
{% trans "Please go to the following page and choose a password:" %}
{% block reset_link %}
{{ protocol }}://{{ domain }}{% url 'login:password_reset_confirm' uidb64=uid token=token %}
{% endblock %}
{% trans "Thanks for using our site!" %}
{% blocktrans %}The {{site}} team{% endblocktrans %}
{% endautoescape %}

View File

@ -0,0 +1,4 @@
{% load i18n %}{% autoescape off %}
{% trans "IdHub" as site %}
{% blocktrans %}User activation on {{site}}{% endblocktrans %}
{% endautoescape %}

View File

@ -42,4 +42,5 @@
<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

@ -9,8 +9,8 @@
<p> <p>
{% block reset_link %} {% block reset_link %}
<a href="{{ protocol }}://{{ domain }}{% url 'idhub:password_reset_confirm' uidb64=uid token=token %}"> <a href="{{ protocol }}://{{ domain }}{% url 'login:password_reset_confirm' uidb64=uid token=token %}">
{{ protocol }}://{{ domain }}{% url 'idhub:password_reset_confirm' uidb64=uid token=token %} {{ protocol }}://{{ domain }}{% url 'login:password_reset_confirm' uidb64=uid token=token %}
</a> </a>
{% endblock %} {% endblock %}
</p> </p>

View File

@ -3,7 +3,7 @@
{% trans "Please go to the following page and choose a new password:" %} {% trans "Please go to the following page and choose a new password:" %}
{% block reset_link %} {% block reset_link %}
{{ protocol }}://{{ domain }}{% url 'idhub:password_reset_confirm' uidb64=uid token=token %} {{ protocol }}://{{ domain }}{% url 'login:password_reset_confirm' uidb64=uid token=token %}
{% endblock %} {% endblock %}
{% trans "Your username, in case you've forgotten:" %} {{ user.username }} {% trans "Your username, in case you've forgotten:" %} {{ user.username }}

View File

@ -1,5 +1,6 @@
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
@ -17,7 +18,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,7 +67,8 @@ class PasswordResetView(auth_views.PasswordResetView):
def form_valid(self, form): def form_valid(self, form):
try: try:
return super().form_valid(form) response = super().form_valid(form)
return response
except Exception as err: except Exception as err:
logger.error(err) logger.error(err)
return HttpResponseRedirect(self.success_url) return HttpResponseRedirect(self.success_url)

View File

@ -17,15 +17,17 @@ 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) self.create_user(institution, email, password, is_admin, predefined_token)
def create_user(self, institution, email, password, is_admin): def create_user(self, institution, email, password, is_admin, predefined_token):
self.u = User.objects.create( self.u = User.objects.create(
institution=institution, institution=institution,
email=email, email=email,
@ -34,6 +36,10 @@ class Command(BaseCommand):
) )
self.u.set_password(password) self.u.set_password(password)
self.u.save() self.u.save()
token = uuid4() if predefined_token:
token = predefined_token
else:
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}")

43
utils/save_snapshots.py Normal file
View File

@ -0,0 +1,43 @@
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