Skip to content
This repository has been archived by the owner on Sep 16, 2022. It is now read-only.

Commit

Permalink
Merge pull request #221 from GreatFruitOmsk/issue-167
Browse files Browse the repository at this point in the history
Credentials Editor UI
  • Loading branch information
vpetersson authored May 29, 2019
2 parents a2f57ea + 9442bb2 commit 351b92a
Show file tree
Hide file tree
Showing 12 changed files with 431 additions and 14 deletions.
8 changes: 7 additions & 1 deletion backend/device_registry/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from django_json_widget.widgets import JSONEditorWidget
from django.contrib.postgres.fields import JSONField

from device_registry.models import Device, DeviceInfo, PortScan, FirewallState
from device_registry.models import Device, DeviceInfo, PortScan, FirewallState, Credential


@admin.register(Device)
Expand Down Expand Up @@ -63,3 +63,9 @@ class FirewallStateAdmin(admin.ModelAdmin):
]

ordering = ('scan_date',)


@admin.register(Credential)
class CredentialAdmin(admin.ModelAdmin):
list_display = ['owner', 'name', 'key', 'value']
list_filter = ['owner']
68 changes: 65 additions & 3 deletions backend/device_registry/api_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
from netaddr import IPAddress

from device_registry import ca_helper
from device_registry.serializers import DeviceInfoSerializer
from device_registry.forms import CredentialsForm
from device_registry.serializers import DeviceInfoSerializer, CredentialSerializer
from device_registry.datastore_helper import datastore_client, dicts_to_ds_entities
from .models import Device, DeviceInfo, FirewallState, PortScan

from .models import Device, DeviceInfo, FirewallState, PortScan, Credential

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -474,6 +474,68 @@ def claim_by_link(request):
return Response('Device not found', status=status.HTTP_404_NOT_FOUND)


@api_view(['GET'])
@permission_classes((permissions.AllowAny,))
def mtls_creds_view(request, format=None):
"""
Return all user's credentials.
"""
device_id = is_mtls_authenticated(request)

if not device_id:
return Response(
'Invalid request.',
status=status.HTTP_400_BAD_REQUEST
)
if type(device_id) is Response:
return device_id

device = Device.objects.get(device_id=device_id)
serializer = CredentialSerializer(device.owner.credentials.all(), many=True)
return Response(serializer.data)


@api_view(['GET', 'POST'])
@permission_classes((permissions.IsAuthenticated,))
def ajax_creds_view(request, format=None):
"""
Return all user's credentials.
"""

if request.method == 'GET':
serializer = CredentialSerializer(request.user.credentials.all(), many=True)
return Response({'data': serializer.data})
elif request.method == 'POST':
d = request.data
if not d.get('method'):
return Response({'error': 'Invalid request: missing method'})
method = d['method']
if method == 'delete':
if not d.get('pk'):
return Response({'error': 'Invalid request: missing pk'})
cred = Credential.objects.get(pk=d['pk'], owner=request.user)
cred.delete()
else:
creds_form = CredentialsForm(request.POST)
if not creds_form.is_valid():
return Response({'error': 'Invalid data supplied'})
try:
if method == 'update':
if not d.get('pk'):
return Response({'error': 'Invalid request: missing pk'})
cred = Credential.objects.get(pk=d['pk'], owner=request.user)
for k in ('name', 'key', 'value'):
setattr(cred, k, d[k])
cred.save()
elif method == 'create':
cred = {k: d[k] for k in ('name', 'key', 'value') }
cred['owner'] = request.user
Credential.objects.create(**cred)
except IntegrityError:
return Response({'error': 'Name/Key combo should be unique'})
return Response({})


@api_view(['GET'])
@permission_classes((permissions.AllowAny,))
def mtls_is_claimed_view(request, format=None):
Expand Down
8 changes: 8 additions & 0 deletions backend/device_registry/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,11 @@ def __init__(self, *args, **kwargs):
open_connections_choices = kwargs.pop('open_connections_choices')
super().__init__(*args, **kwargs)
self.fields['open_connections'].choices = open_connections_choices


class CredentialsForm(forms.Form):
name = forms.CharField(max_length=64)
key = forms.CharField(max_length=64)
value = forms.CharField(max_length=1024)
pk = forms.IntegerField(required=False)
method = forms.CharField(max_length=8)
34 changes: 34 additions & 0 deletions backend/device_registry/migrations/0035_auto_20190528_1300.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Generated by Django 2.1.7 on 2019-05-28 13:00

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('device_registry', '0034_device_name'),
]

operations = [
migrations.CreateModel(
name='Credential',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=64)),
('key', models.CharField(max_length=64)),
('value', models.CharField(max_length=1024)),
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='credentials', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'credentials record',
'verbose_name_plural': 'credentials records',
},
),
migrations.AlterUniqueTogether(
name='credential',
unique_together={('owner', 'key', 'name')},
),
]
14 changes: 14 additions & 0 deletions backend/device_registry/migrations/0036_merge_20190529_1023.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Generated by Django 2.1.7 on 2019-05-29 10:23

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('device_registry', '0035_fix_missing_protocol_version'),
('device_registry', '0035_auto_20190528_1300'),
]

operations = [
]
15 changes: 15 additions & 0 deletions backend/device_registry/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,21 @@ def beautified_rules(self):
return yaml.dump(self.rules) if self.rules else "none"


class Credential(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='credentials', on_delete=models.CASCADE)
name = models.CharField(max_length=64)
key = models.CharField(max_length=64)
value = models.CharField(max_length=1024)

class Meta:
unique_together = ['owner', 'key', 'name']
verbose_name = 'credentials record'
verbose_name_plural = 'credentials records'

def __str__(self):
return f'{self.name}: {self.key}={self.value}'


# Temporary POJO to showcase recommended actions template.
class Action:
def __init__(self, action_id, title, description, actions):
Expand Down
8 changes: 7 additions & 1 deletion backend/device_registry/serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from rest_framework import serializers
from device_registry.models import Device, DeviceInfo
from device_registry.models import Device, DeviceInfo, Credential


class DeviceSerializer(serializers.ModelSerializer):
Expand All @@ -13,3 +13,9 @@ class DeviceInfoSerializer(serializers.ModelSerializer):
class Meta:
model = DeviceInfo
fields = '__all__'


class CredentialSerializer(serializers.ModelSerializer):
class Meta:
model = Credential
fields = ['name', 'key', 'value', 'pk']
1 change: 1 addition & 0 deletions backend/device_registry/templates/admin_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<ul id="dashboards" class="sidebar-dropdown list-unstyled collapse show">
<li class="sidebar-item"><a class="sidebar-link" href="{% url 'claim-device' %}">Claim Device</a></li>
<li class="sidebar-item"><a class="sidebar-link" href="{% url 'actions' %}">Recommended Actions</a></li>
<li class="sidebar-item"><a class="sidebar-link" href="{% url 'credentials' %}">Credentials</a></li>
</ul>
</li>
</ul>
Expand Down
Loading

0 comments on commit 351b92a

Please sign in to comment.