Skip to content

Commit

Permalink
Update the django requirements to 3.0
Browse files Browse the repository at this point in the history
The tests already use django 3+ so we should align this everywhere.

Signed-off-by: Peter Robinson <[email protected]>

Add a guide to setup zezere instance

Signed-off-by: rdotjain <[email protected]>

Update .gitignore for conf file

Signed-off-by: rdotjain <[email protected]>

Add endpoint to upload ov

Signed-off-by: rdotjain <[email protected]>

Update endpoint to accept CBOR encoded vouchers

Signed-off-by: rdotjain <[email protected]>

Improve error listing in UI using messages

Signed-off-by: rdotjain <[email protected]>

tweak endpoint to accept multiple vouchers

Signed-off-by: rdotjain <[email protected]>

bug fix: navbar active tab

Signed-off-by: rdotjain <[email protected]>

Add error handling in API call

Signed-off-by: rdotjain <[email protected]>

ui: change button style

Signed-off-by: rdotjain <[email protected]>

update template for multiple ownership vouchers

Signed-off-by: rdotjain <[email protected]>

add server base url in default conf

Signed-off-by: rdotjain <[email protected]>
  • Loading branch information
nullr0ute authored and rdotjain committed Aug 26, 2022
1 parent 0493501 commit decdbbe
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 10 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,6 @@ venv.bak/

# mypy
.mypy_cache/

# conf file
zezere.conf
81 changes: 81 additions & 0 deletions SETUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Setup a zezere instance locally

1. To install requirements in a Python virtual environment, set it up first.
```sh
$ virtualenv venv
$ . venv/bin/activate
```

2. Before installing other Python requirements, you need to install Apache httpd first. Follow the instructions from [mod-wsgi project documentation](https://pypi.org/project/mod-wsgi/).
<br>
In order to satisfy the `psycopg2` dependency please follow instructions from
[psycopg2 project documentation](https://www.psycopg.org/docs/install.html).

3. Install the requirements
```sh
$ (venv) pip install .
```

4. Before using the `zezere-manage` tool, a configuration needs to be created.
Default configuration can be used as a base:

```
$ cp zezere/default.conf ./zezere.conf
```

5. Authentication method and secret key needs to be set in order to satisfy the
tool. Also, make sure that the allowed_hosts is what you want.

```
allowed_hosts = localhost, 127.0.0.1
secret_key = very-secret
auth_method = local
```

6. Now run the migrations, to create a database file.
```sh
$ python manage.py migrate --noinput
```

7. To collect the static files, run
```
$ python manage.py collectstatic
```

8. Now we can create a superuser:

```
$ zezere-manage createsuperuser --username admin --email [email protected]
```

9. After a password has been set, we are ready to run Zezere:

```
./app.sh
```

Use the admin credentials we created to login to localhost:8080

<br>

# Setup using Docker
The easiest way to run Zezere is to run the official container and authenticate
with OpenID Connect:

```
$ docker run --detach --rm --name zezere \
-e OIDC_RP_CLIENT_ID=<client id> \
-e OIDC_RP_CLIENT_SECRET=<client secret> \
-e OIDC_OP_AUTHORIZATION_ENDPOINT=<authorization endpoint> \
-e OIDC_OP_TOKEN_ENDPOINT=<token endpoint> \
-e OIDC_OP_USER_ENDPOINT=<userinfo endpoint> \
-e OIDC_OP_JWKS_ENDPOINT=<jwks endpoint> \
-e AUTH_METHOD=oidc \
-e SECRET_KEY=localtest \
-e ALLOWED_HOSTS=localhost \
-p 8080:8080 \
-t quay.io/fedora-iot/zezere:latest
```
The default signing algorithm is `RS256` but it can also be controlled with the
environment variable `OIDC_OP_SIGN_ALGO`
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
django>=2.1
django>=3.0
djangorestframework
django-ipware
psycopg2
Expand Down
1 change: 1 addition & 0 deletions zezere/default.conf
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ debug = no
allowed_hosts = localhost, localhost.localdomain
secure_cookie = yes
# auth_method = local, oidc
# ov_base_url =

[oidc.rp]
# client_id =
Expand Down
11 changes: 11 additions & 0 deletions zezere/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import os

from django.contrib.messages import constants as messages

from .settings_external import get, getboolean
from .settings_auth import AUTH_INFO
Expand Down Expand Up @@ -161,3 +162,13 @@
"django.contrib.staticfiles.finders.FileSystemFinder",
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
]

MESSAGE_TAGS = {
messages.DEBUG: "alert-secondary",
messages.INFO: "alert-info",
messages.SUCCESS: "alert-success",
messages.WARNING: "alert-warning",
messages.ERROR: "alert-danger",
}

OV_BASE_URL = get("global", "ov_base_url", "OV_BASE_URL")
14 changes: 9 additions & 5 deletions zezere/templates/portal/master.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,21 @@
</ul>
<ul class="nav navbar-nav navbar-primary">
{% if user.is_authenticated %}
<li class="active">
<a href="/portal" class="active">Home</a>
<li class="{% if nbar == 'portal' %}active{% endif %}">
<a href="/portal">Home</a>
</li>
<li>
<li class="{% if nbar == 'claim' %}active{% endif %}">
<a href="/portal/claim">Claim Unowned Devices</a>
</li>
<li>
<li class="{% if nbar == 'sshkeys' %}active{% endif %}">
<a href="/portal/sshkeys">SSH Key Management</a>
</li>
<li>
<li class="{% if nbar == 'devices' %}active{% endif %}">
<a href="/portal/devices">Device Management</a>
</li>
<li class="{% if nbar == 'ov' %}active{% endif %}">
<a href="/portal/ov">Add Ownership Voucher</a>
</li>
{% else %}
<li>
<a href="/accounts/login">Log In</a>
Expand All @@ -67,6 +70,7 @@

<div class="container">
{% block content %}
{% include 'main/includes/messages.html' %}
Master content
{% endblock %}
</div>
Expand Down
26 changes: 26 additions & 0 deletions zezere/templates/portal/ownership_voucher.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{% extends "./master.html" %}

{% load rules %}

{% block title %}Add Voucher{% endblock %}

{% block content %}

{% if messages %}
{% for message in messages %}
<div{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</div>
{% endfor %}
{% endif %}

Add a new ownership voucher:
<form method="POST" action="/portal/ov/add/" enctype="multipart/form-data">
{% csrf_token %}
Enter the number of vouchers: <input type="number" name="no_of_vouchers" required> <br> <br>
Enter your voucher contents:<br>
<textarea name="ov" rows="10" cols="50" ></textarea> <br> <br>
OR <br> <br>
Upload a CBOR encoded voucher: <input type="file" name="ov_file" multiple> <br><br>
<input type="submit" class="btn btn-primary btn-sm" value="Add voucher"> <br>
</form>

{% endblock %}
3 changes: 3 additions & 0 deletions zezere/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
views_portal.remove_ssh_key,
name="portal_sshkeys_remove",
),
path("portal/ov/", views_portal.ov, name="portal_ov"),
path("portal/ov/add/", views_portal.add_ov, name="portal_ov_add"),
# path("portal/ov/delete/", views_portal.remove_ov, name="portal_ov_remove"),
# API
path("api/", include(router.urls), name="apis"),
path(
Expand Down
66 changes: 62 additions & 4 deletions zezere/views_portal.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import requests

from django.core.exceptions import PermissionDenied
from django.contrib import messages
from django.db import transaction
from django.http import Http404, HttpResponseBadRequest
from django.shortcuts import render, get_object_or_404, redirect
Expand All @@ -9,11 +12,12 @@
from ipware import get_client_ip

from zezere.models import Device, RunRequest, device_getter, SSHKey
from zezere.settings import OV_BASE_URL


@login_required
def index(request):
return render(request, "portal/index.html")
return render(request, "portal/index.html", {"nbar": "portal"})


@login_required
Expand All @@ -33,14 +37,20 @@ def claim(request):
else:
remote_ip, _ = get_client_ip(request)
unclaimed = Device.objects.filter(owner__isnull=True, last_ip_address=remote_ip)
context = {"unclaimed_devices": unclaimed, "super": request.user.is_superuser}
context = {
"unclaimed_devices": unclaimed,
"super": request.user.is_superuser,
"nbar": "claim",
}
return render(request, "portal/claim.html", context)


@login_required
def devices(request):
devices = Device.objects.filter(owner=request.user)
return render(request, "portal/devices.html", {"devices": devices})
return render(
request, "portal/devices.html", {"devices": devices, "nbar": "devices"}
)


@permission_required(Device.get_perm("provision"), fn=device_getter)
Expand Down Expand Up @@ -79,7 +89,9 @@ def clean_runreq(request, mac_addr):
@login_required
def sshkeys(request):
sshkeys = SSHKey.objects.filter(owner=request.user)
return render(request, "portal/sshkeys.html", {"sshkeys": sshkeys})
return render(
request, "portal/sshkeys.html", {"sshkeys": sshkeys, "nbar": "sshkeys"}
)


@login_required
Expand All @@ -104,3 +116,49 @@ def add_ssh_key(request):
key.full_clean()
key.save()
return redirect("portal_sshkeys")


@login_required
def ov(request):
return render(request, "portal/ownership_voucher.html", {"nbar": "ov"})


@login_required
@require_POST
def add_ov(request):
payload = None
no_of_vouchers = request.POST["no_of_vouchers"]

if request.POST.get("ov"):
content_type = "application/x-pem-file"
payload = request.POST["ov"].strip()
elif request.FILES.getlist("ov_file"):
content_type = "application/cbor"
files = request.FILES.getlist("ov_file")
payload = files[0].read()
for f in files[1:]:
payload += f.read()
else:
messages.error(request, "No ownership voucher provided")
return redirect("portal_ov")

url = OV_BASE_URL
headers = {
"X-Number-Of-Vouchers": no_of_vouchers,
"Content-Type": content_type,
"Authorization": "Bearer",
}

try:
response = requests.request("POST", url, headers=headers, data=payload)
except requests.exceptions.RequestException as e:
messages.error(request, "Error while adding ownership voucher: {}".format(e))
return redirect("portal_ov")

if response.status_code == 201:
messages.success(request, "Ownership voucher added")
else:
error_code = response.json().get("error_code")
messages.error(request, "Error adding ownership voucher: {}".format(error_code))

return redirect("portal_ov")

0 comments on commit decdbbe

Please sign in to comment.