feat(itam): migrate app from own repo

!1
This commit is contained in:
2024-05-15 20:55:30 +09:30
parent f98e3bc9c2
commit 195bb5e4ab
33 changed files with 1303 additions and 1 deletions

1
.gitignore vendored
View File

@ -4,4 +4,3 @@ __pycache__
**db.sqlite3
**.coverage
artifacts/
**itam/

View File

@ -0,0 +1,23 @@
from rest_framework import serializers
from itam.models.device import Device
class DeviceSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name="_api_device_view", format="html"
)
config = serializers.SerializerMethodField('get_device_config')
def get_device_config(self, device):
return device.get_configuration(device.id)
class Meta:
model = Device
fields = '__all__'

View File

@ -0,0 +1,15 @@
from rest_framework import serializers
from itam.models.device import Software
class SoftwareSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name="_api_software_view", format="html"
)
class Meta:
model = Software
fields = '__all__'

View File

@ -3,6 +3,7 @@ from rest_framework.urlpatterns import format_suffix_patterns
from .views import access, index
from .views.itam import device as itam_device, software as itam_software, config as itam_config
urlpatterns = [
path("", index.IndexView.as_view(), name='_api_home'),
@ -11,6 +12,15 @@ urlpatterns = [
path("organization/<int:organization_id>/team/<int:group_ptr_id>/", access.TeamDetail.as_view(), name='_api_team'),
path("organization/team/", access.TeamList.as_view(), name='_api_teams'),
path("config/<str:device_name>/", itam_config.View.as_view(), name="_api_device_config"),
path("device/", itam_device.List.as_view(), name="_api_devices"),
path("device/<int:pk>/", itam_device.Detail.as_view(), name="_api_device_view"),
path("software/", itam_software.List.as_view(), name="_api_softwares"),
path("software/<int:pk>/", itam_software.Detail.as_view(), name="_api_software_view"),
]
urlpatterns = format_suffix_patterns(urlpatterns)

View File

@ -29,5 +29,7 @@ class IndexView(PermissionRequiredMixin, LoginRequiredMixin, routers.APIRootView
{
"organizations": reverse("_api_orgs", request=request),
"teams": reverse("_api_teams", request=request),
"devices": reverse("_api_devices", request=request),
"software": reverse("_api_softwares", request=request),
}
)

View File

View File

@ -0,0 +1,19 @@
from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin
from itam.models.device import Device
from rest_framework import views
from rest_framework.response import Response
class View(views.APIView):
def get(self, request, device_name):
device = Device.objects.get(slug=device_name)
return Response(device.get_configuration(device.id))
def get_view_name(self):
return "Device Config"

View File

@ -0,0 +1,24 @@
from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin
from rest_framework import generics
from itam.models.device import Device
from api.serializers.itam.device import DeviceSerializer
class List(PermissionRequiredMixin, LoginRequiredMixin, generics.ListCreateAPIView):
permission_required = 'itam.view_device'
queryset = Device.objects.all()
serializer_class = DeviceSerializer
def get_view_name(self):
return "Devices"
class Detail(PermissionRequiredMixin, LoginRequiredMixin, generics.RetrieveUpdateDestroyAPIView):
permission_required = 'itam.view_device'
queryset = Device.objects.all()
serializer_class = DeviceSerializer
def get_view_name(self):
return "Device"

View File

@ -0,0 +1,24 @@
from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin
from rest_framework import generics
from itam.models.software import Software
from api.serializers.itam.software import SoftwareSerializer
class List(PermissionRequiredMixin, LoginRequiredMixin, generics.ListCreateAPIView):
permission_required = 'itam.view_software'
queryset = Software.objects.all()
serializer_class = SoftwareSerializer
def get_view_name(self):
return "Softwares"
class Detail(PermissionRequiredMixin, LoginRequiredMixin, generics.RetrieveUpdateDestroyAPIView):
permission_required = 'itam.view_software'
queryset = Software.objects.all()
serializer_class = SoftwareSerializer
def get_view_name(self):
return "Software"

View File

@ -43,6 +43,7 @@ INSTALLED_APPS = [
'social_django',
'core.apps.CoreConfig',
'access.apps.AccessConfig',
'itam.apps.ItamConfig',
]
MIDDLEWARE = [

View File

@ -28,6 +28,7 @@ urlpatterns = [
name="change_password"),
path("account/", include("django.contrib.auth.urls")),
path("organization/", include("access.urls")),
path("itam/", include("itam.urls")),
]

0
app/itam/__init__.py Normal file
View File

6
app/itam/apps.py Normal file
View File

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

View File

View File

@ -0,0 +1,21 @@
from django import forms
from django.db.models import Q
from itam.models.device import DeviceSoftware
from itam.models.software import Software
class SoftwareAdd(forms.ModelForm):
class Meta:
model = DeviceSoftware
fields = [
'software',
'action'
]
def __init__(self, *args, **kwargs):
organizations = kwargs.pop('organizations')
super().__init__(*args, **kwargs)
self.fields['software'].queryset = Software.objects.filter(Q(organization_id__in=organizations) | Q(is_global = True))

View File

View File

@ -0,0 +1,23 @@
from django import forms
from django.db.models import Q
from itam.models.software import Software
class Update(forms.ModelForm):
class Meta:
model = Software
fields = [
"name",
'slug',
'id',
'organization',
'is_global',
'category',
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['is_global'].widget.attrs['disabled'] = True

View File

@ -0,0 +1,118 @@
# Generated by Django 5.0.6 on 2024-05-15 06:10
import access.fields
import django.db.models.deletion
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('access', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='DeviceType',
fields=[
('is_global', models.BooleanField(default=False)),
('id', models.AutoField(primary_key=True, serialize=False, unique=True)),
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False)),
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False)),
('name', models.CharField(max_length=50, unique=True)),
('slug', access.fields.AutoSlugField()),
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='access.organization')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Device',
fields=[
('is_global', models.BooleanField(default=False)),
('id', models.AutoField(primary_key=True, serialize=False, unique=True)),
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False)),
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False)),
('name', models.CharField(max_length=50, unique=True)),
('slug', access.fields.AutoSlugField()),
('serial_number', models.CharField(blank=True, default=None, max_length=50, null=True, verbose_name='Serial Number')),
('uuid', models.CharField(blank=True, default=None, max_length=50, null=True, verbose_name='UUID')),
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='access.organization')),
('device_type', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='itam.devicetype')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Software',
fields=[
('is_global', models.BooleanField(default=False)),
('id', models.AutoField(primary_key=True, serialize=False, unique=True)),
('name', models.CharField(max_length=50, unique=True)),
('slug', access.fields.AutoSlugField()),
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False)),
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False)),
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='access.organization')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='DeviceSoftware',
fields=[
('is_global', models.BooleanField(default=False)),
('id', models.AutoField(primary_key=True, serialize=False, unique=True)),
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False)),
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False)),
('action', models.CharField(choices=[('1', 'Install'), ('0', 'Remove')], default=None, max_length=1)),
('device', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='itam.device')),
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='access.organization')),
('software', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='itam.software')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='SoftwareCategory',
fields=[
('is_global', models.BooleanField(default=False)),
('id', models.AutoField(primary_key=True, serialize=False, unique=True)),
('name', models.CharField(max_length=50, unique=True)),
('slug', access.fields.AutoSlugField()),
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False)),
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False)),
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='access.organization')),
],
options={
'abstract': False,
},
),
migrations.AddField(
model_name='software',
name='category',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='itam.softwarecategory'),
),
migrations.CreateModel(
name='SoftwareVersion',
fields=[
('is_global', models.BooleanField(default=False)),
('id', models.AutoField(primary_key=True, serialize=False, unique=True)),
('name', models.CharField(max_length=50, unique=True)),
('slug', access.fields.AutoSlugField()),
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False)),
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False)),
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='access.organization')),
('software', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='itam.software')),
],
options={
'abstract': False,
},
),
]

View File

142
app/itam/models/device.py Normal file
View File

@ -0,0 +1,142 @@
from django.db import models
from access.fields import *
from access.models import TenancyObject
from itam.models.software import Software
class DeviceCommonFields(TenancyObject, models.Model):
class Meta:
abstract = True
id = models.AutoField(
primary_key=True,
unique=True,
blank=False
)
created = AutoCreatedField()
modified = AutoLastModifiedField()
class DeviceCommonFieldsName(DeviceCommonFields):
class Meta:
abstract = True
name = models.CharField(
blank = False,
max_length = 50,
unique = True,
)
slug = AutoSlugField()
class DeviceType(DeviceCommonFieldsName):
def __str__(self):
return self.name
class Device(DeviceCommonFieldsName):
serial_number = models.CharField(
verbose_name = 'Serial Number',
max_length = 50,
default = None,
null = True,
blank = True
)
uuid = models.CharField(
verbose_name = 'UUID',
max_length = 50,
default = None,
null = True,
blank = True
)
device_type = models.ForeignKey(
DeviceType,
on_delete=models.CASCADE,
default = None,
null = True,
blank= True
)
def __str__(self):
return self.name
def get_configuration(self, id):
softwares = DeviceSoftware.objects.filter(device=id)
config = {
"software": []
}
for software in softwares:
if int(software.action) == 1:
state = 'present'
elif int(software.action) == 0:
state = 'absent'
software = {
"name": software.software.slug,
"state": state
}
config['software'] = config['software'] + [ software ]
return config
class DeviceSoftware(DeviceCommonFields):
""" A way for the device owner to configure software to install/remove """
class Actions(models.TextChoices):
INSTALL = '1', 'Install'
REMOVE = '0', 'Remove'
device = models.ForeignKey(
Device,
on_delete=models.CASCADE,
default = None,
null = False,
blank= False
)
software = models.ForeignKey(
Software,
on_delete=models.CASCADE,
default = None,
null = False,
blank= False
)
action = models.CharField(
max_length=1,
choices=Actions,
default=None,
)

View File

@ -0,0 +1,66 @@
from django.db import models
from access.fields import *
from access.models import TenancyObject
class SoftwareCommonFields(TenancyObject, models.Model):
class Meta:
abstract = True
id = models.AutoField(
primary_key=True,
unique=True,
blank=False
)
name = models.CharField(
blank = False,
max_length = 50,
unique = True,
)
slug = AutoSlugField()
created = AutoCreatedField()
modified = AutoLastModifiedField()
class SoftwareCategory(SoftwareCommonFields):
def __str__(self):
return self.name
class Software(SoftwareCommonFields):
category = models.ForeignKey(
SoftwareCategory,
on_delete=models.CASCADE,
default = None,
null = True,
blank= True
)
def __str__(self):
return self.name
class SoftwareVersion(SoftwareCommonFields):
software = models.ForeignKey(
Software,
on_delete=models.CASCADE,
)

View File

@ -0,0 +1,91 @@
{% extends 'base.html.j2' %}
{% block title %}{{ device.name }}{% endblock %}
{% block body %}
<script>
function openCity(evt, cityName) {
// Declare all variables
var i, tabcontent, tablinks;
// Get all elements with class="tabcontent" and hide them
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
// Get all elements with class="tablinks" and remove the class "active"
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
// Show the current tab, and add an "active" class to the button that opened the tab
document.getElementById(cityName).style.display = "block";
evt.currentTarget.className += " active";
}
</script>
<div class="tab">
<button onclick="window.location='{% url 'ITAM:Devices' %}';">
<< Back to Devices</button>
<button id="defaultOpen" class="tablinks" onclick="openCity(event, 'Details')">Details</button>
<button id="defaultOpen" class="tablinks" onclick="openCity(event, 'OperatingSystem')">Operating System</button>
<button class="tablinks" onclick="openCity(event, 'Software')">Software</button>
<button class="tablinks" onclick="openCity(event, 'ConfigManagement')">Config Management</button>
<!-- <button class="tablinks" onclick="openCity(event, 'Installations')">Installations</button> -->
</div>
<!-- Tab content -->
<div id="Details" class="tabcontent">
<h3>Details</h3>
<form method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
<script>
// Get the element with id="defaultOpen" and click on it
document.getElementById("defaultOpen").click();
</script>
</div>
<div id="OperatingSystem" class="tabcontent">
<h3>Operating System</h3>
</div>
<div id="Software" class="tabcontent">
<h3>Software</h3>
<input type="button" value="Add Software Action" onclick="window.location='{% url 'ITAM:_device_software_add' device.id %}';">
<table>
<thead>
<th>Name</th>
<th>Action</th>
<th>Installed</th>
<th>&nbsp;</th>
</thead>
{% for software in softwares %}
<tr>
<td><a href="{% url 'ITAM:_software_view' pk=software.id %}">{{ software.software }}</a></td>
<td><a href="{% url 'ITAM:_device_software_view' device_id=device.id pk=software.id %}">{{ software.get_action_display }}</a></td>
<td>&nbsp;</td>
<th>&nbsp;</th>
</tr>
{% endfor %}
</table>
</div>
<div id="ConfigManagement" class="tabcontent">
<h3>Configuration Management</h3>
<div>
<textarea cols="90" rows="30" readonly>{{ config }}</textarea>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,31 @@
{% extends 'base.html.j2' %}
{% block title %}Devices{% endblock %}
{% block content_header_icon %}{% endblock %}
{% block body %}
<input type="button" value="New Device" onclick="window.location='{% url 'ITAM:_device_add' %}';">
<input type="button" value="New Device Type" onclick="window.location='{% url 'ITAM:_device_type_add' %}';">
<table class="data">
<tr>
<th>Name</th>
<th>Device Type</th>
<th>Manufacturer</th>
<th>Model</th>
<th>Organization</th>
<th>&nbsp;</th>
</tr>
{% for device in devices %}
<tr>
<td><a href="{% url 'ITAM:_device_view' pk=device.id %}">{{ device.name }}</a></td>
<td>{{ device.device_type }}</td>
<td>manufacturer</td>
<td>model</td>
<td>{% if software.is_global %}Global{% else %}{{ device.organization }}{% endif %}</td>
<td><a href="{% url 'ITAM:_device_delete' pk=device.id %}">Delete</a></td>
</tr>
{% endfor %}
</table>
{% endblock %}

View File

@ -0,0 +1,128 @@
{% extends 'base.html.j2' %}
{% block title %}{{ software.name }}{% endblock %}
{% block body %}
<script>
function openCity(evt, cityName) {
// Declare all variables
var i, tabcontent, tablinks;
// Get all elements with class="tabcontent" and hide them
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
// Get all elements with class="tablinks" and remove the class "active"
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
// Show the current tab, and add an "active" class to the button that opened the tab
document.getElementById(cityName).style.display = "block";
evt.currentTarget.className += " active";
}
</script>
<div class="tab">
<button onclick="window.location='{% url 'ITAM:Software' %}';">
<< Back to Software</button>
<button id="defaultOpen" class="tablinks" onclick="openCity(event, 'Details')">Details</button>
<button id="defaultOpen" class="tablinks" onclick="openCity(event, 'Versions')">Versions</button>
<button class="tablinks" onclick="openCity(event, 'Licences')">Licences</button>
<button class="tablinks" onclick="openCity(event, 'Installations')">Installations</button>
</div>
<!-- Tab content -->
<div id="Details" class="tabcontent">
<h3>Details</h3>
<form method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
<script>
// Get the element with id="defaultOpen" and click on it
document.getElementById("defaultOpen").click();
</script>
</div>
<div id="Versions" class="tabcontent">
<h3>Versions</h3>
Not Yet Implemented
<table>
<thead>
<th>Version</th>
<th>Installations</th>
<th>Vulnerable</th>
<th>&nbsp;</th>
</thead>
<tr>
<td>1.0.0</td>
<td>5</td>
<td><input type="checkbox" checked disabled></td>
</tr>
</table>
</div>
<div id="Licences" class="tabcontent">
<h3>Licences</h3>
Not Yet Implemented
<table>
<thead>
<th>Name</th>
<th>Type</th>
<th>Available</th>
<th>&nbsp;</th>
</thead>
<tr>
<td>GPL-3</td>
<td>Open Source</td>
<td>1 / 5</td>
<th>&nbsp;</th>
</tr>
<tr>
<td>MIT</td>
<td>Open Source</td>
<td>Unlimited</td>
<th>&nbsp;</th>
</tr>
<tr>
<td>Windows Device</td>
<td>CAL</td>
<td>11 / 15</td>
<th>&nbsp;</th>
</tr>
</table>
</div>
<div id="Installations" class="tabcontent">
<h3>Installations</h3>
Dev Notes: This table will show joined tables installed software and device software action as a single row.
<table>
<thead>
<th>Device</th>
<th title="Not Set/Install/Remove">Action</th>
<th title="Date Software Installed">Installed</th>
<th>&nbsp;</th>
</thead>
{% for device in device_software %}
<tr>
<td><a href="{% url 'ITAM:_device_view' pk=device.device.id %}">{{ device.device }}</a></td>
<td>{{ device.get_action_display }}</td>
<td>Not Implemented</td>
<td>&nbsp;</td>
</tr>
{% endfor %}
</table>
</div>
{% endblock %}

View File

@ -0,0 +1,29 @@
{% extends 'base.html.j2' %}
{% block title %}Software{% endblock %}
{% block content_header_icon %}{% endblock %}
{% block body %}
<input type="button" value="New Software" onclick="window.location='{% url 'ITAM:_software_add' %}';">
<input type="button" value="New Category" onclick="window.location='{% url 'ITAM:_software_category_add' %}';">
<table class="data">
<tr>
<th>Name</th>
<th>Created</th>
<th>Modified</th>
<th>Organization</th>
<th>&nbsp;</th>
</tr>
{% for software in softwares %}
<tr>
<td><a href="{% url 'ITAM:_software_view' pk=software.id %}">{{ software.name }}</a></td>
<td>{{ software.created }}</td>
<td>{{ software.modified }}</td>
<td>{% if software.is_global %}Global{% else %}{{ software.organization }}{% endif %}</td>
<td><a href="{% url 'ITAM:_software_delete' pk=software.id %}">Delete</a></td>
</tr>
{% endfor %}
</table>
{% endblock %}

View File

@ -0,0 +1,22 @@
# from django.conf import settings
# from django.shortcuts import reverse
from django.test import TestCase, Client
import pytest
import unittest
import requests
# from django.contrib.auth import get_user_model
# from django.core.exceptions import ValidationError
# from access.models import Organization
# class Test_app_structure_auth(unittest.TestCase):
# User = get_user_model()
@pytest.mark.skip(reason="to be written")
def test_device_software_action(user):
"""Ensure only software that is from the same organization or is global can be added to the device
"""
pass

View File

@ -0,0 +1,22 @@
# from django.conf import settings
# from django.shortcuts import reverse
from django.test import TestCase, Client
import pytest
import unittest
import requests
# from django.contrib.auth import get_user_model
# from django.core.exceptions import ValidationError
# from access.models import Organization
# class Test_app_structure_auth(unittest.TestCase):
@pytest.mark.skip(reason="to be written")
def test_software_update_is_global_no_change(user):
"""Once software is set to global it can't be changed.
global status can't be changed as non-global items may reference the item.
"""
pass

27
app/itam/urls.py Normal file
View File

@ -0,0 +1,27 @@
from django.urls import path
from . import views
from .views import device, device_type, software, software_category
app_name = "ITAM"
urlpatterns = [
path("device/", device.IndexView.as_view(), name="Devices"),
path("device/<int:pk>/", device.View.as_view(), name="_device_view"),
path("device/<int:pk>/software/add", device.SoftwareAdd.as_view(), name="_device_software_add"),
path("device/<int:device_id>/software/<int:pk>", device.SoftwareView.as_view(), name="_device_software_view"),
path("device/<int:pk>/delete", device.Delete.as_view(), name="_device_delete"),
path("device/add/", device.Add.as_view(), name="_device_add"),
path("device_type/add/", device_type.Add.as_view(), name="_device_type_add"),
path("software/", software.IndexView.as_view(), name="Software"),
path("software/<int:pk>/", software.View.as_view(), name="_software_view"),
path("software/<int:pk>/delete", software.Delete.as_view(), name="_software_delete"),
path("software/add/", software.Add.as_view(), name="_software_add"),
path("software_category/add/", software_category.Add.as_view(), name="_software_category_add"),
]

View File

189
app/itam/views/device.py Normal file
View File

@ -0,0 +1,189 @@
import json
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db.models import Q
from django.views import generic
from access.mixin import OrganizationPermission
from access.models import Organization
from ..models.device import Device, DeviceSoftware
from itam.forms.device_softwareadd import SoftwareAdd
class IndexView(PermissionRequiredMixin, OrganizationPermission, generic.ListView):
model = Device
permission_required = 'itam.view_device'
template_name = 'itam/device_index.html.j2'
context_object_name = "devices"
def get_queryset(self):
if self.request.user.is_superuser:
return Device.objects.filter().order_by('name')
else:
return Device.objects.filter(Q(organization__in=self.user_organizations()) | Q(is_global = True)).order_by('name')
class View(OrganizationPermission, generic.UpdateView):
model = Device
permission_required = [
'itam.view_device'
]
template_name = 'itam/device.html.j2'
fields = [
'id',
'name',
'serial_number',
'uuid',
'device_type',
'is_global'
]
context_object_name = "device"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
softwares = DeviceSoftware.objects.filter(device=self.kwargs['pk'])
context['content_title'] = self.object.name
context['softwares'] = softwares
config = self.object.get_configuration(self.kwargs['pk'])
context['config'] = json.dumps(config, indent=4, sort_keys=True)
return context
def get_success_url(self, **kwargs):
return f"/itam/device/{self.kwargs['pk']}/"
class SoftwareView(OrganizationPermission, generic.UpdateView):
model = DeviceSoftware
permission_required = [
'itam.view_devicesoftware'
]
template_name = 'form.html.j2'
fields = [
'action',
]
context_object_name = "devicesoftware"
def form_valid(self, form):
device = Device.objects.get(pk=self.kwargs['device_id'])
form.instance.organization_id = device.organization.id
form.instance.device_id = self.kwargs['device_id']
return super().form_valid(form)
def get_success_url(self, **kwargs):
return f"/itam/device/{self.kwargs['device_id']}/"
class Add(PermissionRequiredMixin, OrganizationPermission, generic.CreateView):
model = Device
permission_required = [
'itam.add_device',
]
template_name = 'form.html.j2'
fields = [
'name',
'serial_number',
'uuid',
'device_type',
'organization',
'is_global'
]
def get_success_url(self, **kwargs):
return f"/itam/device/"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = 'Add Device'
return context
class SoftwareAdd(PermissionRequiredMixin, OrganizationPermission, generic.CreateView):
model = DeviceSoftware
permission_required = [
'itam.add_devicesoftware',
]
template_name = 'form.html.j2'
# fields = [
# 'software',
# 'action'
# ]
form_class = SoftwareAdd
def form_valid(self, form):
device = Device.objects.get(pk=self.kwargs['pk'])
print(f'HERE={device.organization.id}')
form.instance.organization_id = device.organization.id
form.instance.device_id = self.kwargs['pk']
return super().form_valid(form)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['organizations'] = self.user_organizations()
return kwargs
def get_success_url(self, **kwargs):
return f"/itam/device/{self.kwargs['pk']}/"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = 'Add Device'
return context
class Delete(PermissionRequiredMixin, OrganizationPermission, generic.DeleteView):
model = Device
permission_required = [
'itam.delete_device',
]
template_name = 'form.html.j2'
def get_success_url(self, **kwargs):
return f"/itam/device/"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = 'Delete ' + self.object.name
return context

View File

@ -0,0 +1,63 @@
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.views import generic
from access.mixin import OrganizationPermission
from ..models.device import DeviceType
class View(OrganizationPermission, generic.UpdateView):
model = DeviceType
permission_required = [
'itam.view_device_type'
]
template_name = 'form.html.j2'
fields = [
"name",
'slug',
'id',
'organization',
'is_global',
]
context_object_name = "device_category"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = self.object.name
return context
def get_success_url(self, **kwargs):
return f"/itam/device/{self.kwargs['pk']}/"
class Add(PermissionRequiredMixin, OrganizationPermission, generic.CreateView):
model = DeviceType
permission_required = [
'access.add_device_type',
]
template_name = 'form.html.j2'
fields = [
'name',
'organization',
'is_global'
]
def get_success_url(self, **kwargs):
return f"/itam/device/"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = 'Add Device Type'
return context

100
app/itam/views/software.py Normal file
View File

@ -0,0 +1,100 @@
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db.models import Q
from django.views import generic
from access.mixin import OrganizationPermission
from itam.models.device import DeviceSoftware
from itam.models.software import Software
from itam.forms.software.update import Update as SoftwareUpdate_Form
class IndexView(PermissionRequiredMixin, OrganizationPermission, generic.ListView):
model = Software
permission_required = 'itam.view_software'
template_name = 'itam/software_index.html.j2'
context_object_name = "softwares"
def get_queryset(self):
if self.request.user.is_superuser:
return Software.objects.filter().order_by('name')
else:
return Software.objects.filter(Q(organization__in=self.user_organizations()) | Q(is_global = True)).order_by('name')
class View(OrganizationPermission, generic.UpdateView):
model = Software
permission_required = [
'itam.view_software'
]
template_name = 'itam/software.html.j2'
form_class = SoftwareUpdate_Form
context_object_name = "software"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = self.object.name
context['device_software'] = DeviceSoftware.objects.filter(Q(device__in=self.user_organizations(), software=self.kwargs['pk']))
return context
def get_success_url(self, **kwargs):
return f"/itam/software/{self.kwargs['pk']}/"
class Add(PermissionRequiredMixin, OrganizationPermission, generic.CreateView):
model = Software
permission_required = [
'access.add_software',
]
template_name = 'form.html.j2'
fields = [
'name',
'category',
'organization',
'is_global'
]
def get_success_url(self, **kwargs):
return f"/itam/software/"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = 'Add Software'
return context
class Delete(PermissionRequiredMixin, OrganizationPermission, generic.DeleteView):
model = Software
permission_required = [
'access.delete_software',
]
template_name = 'form.html.j2'
def get_success_url(self, **kwargs):
return f"/itam/software/"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = 'Delete ' + self.object.name
return context

View File

@ -0,0 +1,106 @@
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.views import generic
from access.mixin import OrganizationPermission
from ..models.software import Software, SoftwareCategory
class IndexView(PermissionRequiredMixin, OrganizationPermission, generic.ListView):
model = Software
permission_required = 'itam.view_software'
template_name = 'itam/software_index.html.j2'
context_object_name = "softwares"
def get_queryset(self):
if self.request.user.is_superuser:
return Software.objects.filter().order_by('name')
else:
return Software.objects.filter(organization=self.user_organizations()).order_by('name')
class View(OrganizationPermission, generic.UpdateView):
model = Software
permission_required = [
'itam.view_software'
]
template_name = 'form.html.j2'
fields = [
"name",
'slug',
'id',
'organization',
'is_global',
]
context_object_name = "software"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = self.object.name
return context
def get_success_url(self, **kwargs):
return f"/itam/software/{self.kwargs['pk']}/"
class Add(PermissionRequiredMixin, OrganizationPermission, generic.CreateView):
model = SoftwareCategory
permission_required = [
'access.add_software',
]
template_name = 'form.html.j2'
fields = [
'name',
'organization',
'is_global'
]
def get_success_url(self, **kwargs):
return f"/itam/software/"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = 'Add Software Category'
return context
class Delete(PermissionRequiredMixin, OrganizationPermission, generic.DeleteView):
model = Software
permission_required = [
'access.delete_software',
]
template_name = 'form.html.j2'
# fields = [
# 'name',
# 'organization',
# 'is_global'
# ]
def get_success_url(self, **kwargs):
return f"/itam/software/"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = 'Delete ' + self.object.name
return context