1
0
mirror of https://github.com/peeringdb/peeringdb.git synced 2024-05-11 05:55:09 +00:00
Files
peeringdb-peeringdb/peeringdb_server/admin.py

1692 lines
49 KiB
Python
Raw Normal View History

2018-11-08 19:45:21 +00:00
import datetime
import time
import json
import ipaddress
from . import forms
2018-11-08 19:45:21 +00:00
from operator import or_
import django.urls
2018-11-08 19:45:21 +00:00
from django.conf.urls import url
from django.shortcuts import redirect, Http404
from django.contrib.contenttypes.models import ContentType
from django.contrib import admin, messages
2018-11-08 19:45:21 +00:00
from django.contrib.auth import forms
from django.contrib.admin import helpers
from django.contrib.admin.actions import delete_selected
from django.contrib.admin.views.main import ChangeList
June updates (#751) * Add pointer from API docs to tutorial #650 * Sorting by clicking table headers should use local-compare #356 * Mark IXP peering LAN as bogon #352 * Add help text to "Add (Facility, Network, Exchange)" tab #669 * Add Looking Glass field to the IX object #672 * Add read-only Superuser #679 * Make spelling of traffic levels consistent #519 (#723) * Offer 2FA (#290) * Show "Last Updated" fields on fac, ix, org records (#526) * Enable sort and reverse sort of IP column in IX display (#72) * IRR validation not handling unexpected characters gracefully (#712) * Support alternative direction of writing, e.g. Arabic (#618) * Undeleting an ixlan with an emtpy IPv4 XOR IPv6 field throws a silly error (#644) * Changing org while adding net results in 500 #654 * missing delete button for organisations (#121) * When changing owner of an ix admin GUI borks because of "Ixlan for exchange already exists" #666 * Selection should only present undeleted objects (#664) * change default encoding of API calls to 'utf-8' #663 * Posting https://www.peeringdb.com onto social media doesn't select a good preview image #537 * Revert "Add Looking Glass field to the IX object #672" This reverts commit 4daf2520043c241fabe9a521757efa86a274e28a. Conflicts: peeringdb_server/migrations/0037_ix_looking_glass.py peeringdb_server/views.py * 500 Internal Error when creating IX where prefix already exists elsewhere #718 * Fix graceful restore of soft-deleted objects with translation active (#580) * Don't return any POC data with status=deleted #569 Hard delete soft-deleted pocs after grace period #566 * django-peeringdb from github@2.0.0.2-beta Co-authored-by: Stefan Pratter <stefan@20c.com>
2020-06-24 12:55:01 -05:00
from django.http import HttpResponseForbidden
2018-11-08 19:45:21 +00:00
from django import forms as baseForms
from django.utils import html
2018-11-08 19:45:21 +00:00
from django.core.exceptions import ValidationError
from django.conf import settings
from django.template import loader
from django.template.response import TemplateResponse
from django.db.models import Q
from django.db.models.functions import Concat
2019-12-05 16:57:52 +00:00
from django_namespace_perms.admin import (
UserPermissionInline,
UserPermissionInlineAdd,
UserAdmin,
)
from django.utils.safestring import mark_safe
2018-11-08 19:45:21 +00:00
import reversion
from reversion.admin import VersionAdmin
from django_handleref.admin import VersionAdmin as HandleRefVersionAdmin
2018-11-08 19:45:21 +00:00
import peeringdb_server.admin_commandline_tools as acltools
2019-12-05 16:57:52 +00:00
from peeringdb_server.views import JsonResponse, HttpResponseForbidden
2018-11-08 19:45:21 +00:00
from peeringdb_server.models import (
2019-12-05 16:57:52 +00:00
REFTAG_MAP,
QUEUE_ENABLED,
COMMANDLINE_TOOLS,
OrganizationMerge,
OrganizationMergeEntity,
Sponsorship,
SponsorshipOrganization,
Partnership,
UserOrgAffiliationRequest,
VerificationQueueItem,
Organization,
Facility,
InternetExchange,
Network,
InternetExchangeFacility,
IXLan,
IXLanIXFMemberImportLog,
IXLanIXFMemberImportLogEntry,
IXLanPrefix,
NetworkContact,
NetworkFacility,
NetworkIXLan,
User,
CommandLineTool,
UTC,
DeskProTicket,
)
2018-11-08 19:45:21 +00:00
from peeringdb_server.mail import mail_users_entity_merge
from peeringdb_server.inet import RdapLookup, RdapException
delete_selected.short_description = "HARD DELETE - Proceed with caution"
2018-11-08 19:45:21 +00:00
from django.utils.translation import ugettext_lazy as _
June updates (#751) * Add pointer from API docs to tutorial #650 * Sorting by clicking table headers should use local-compare #356 * Mark IXP peering LAN as bogon #352 * Add help text to "Add (Facility, Network, Exchange)" tab #669 * Add Looking Glass field to the IX object #672 * Add read-only Superuser #679 * Make spelling of traffic levels consistent #519 (#723) * Offer 2FA (#290) * Show "Last Updated" fields on fac, ix, org records (#526) * Enable sort and reverse sort of IP column in IX display (#72) * IRR validation not handling unexpected characters gracefully (#712) * Support alternative direction of writing, e.g. Arabic (#618) * Undeleting an ixlan with an emtpy IPv4 XOR IPv6 field throws a silly error (#644) * Changing org while adding net results in 500 #654 * missing delete button for organisations (#121) * When changing owner of an ix admin GUI borks because of "Ixlan for exchange already exists" #666 * Selection should only present undeleted objects (#664) * change default encoding of API calls to 'utf-8' #663 * Posting https://www.peeringdb.com onto social media doesn't select a good preview image #537 * Revert "Add Looking Glass field to the IX object #672" This reverts commit 4daf2520043c241fabe9a521757efa86a274e28a. Conflicts: peeringdb_server/migrations/0037_ix_looking_glass.py peeringdb_server/views.py * 500 Internal Error when creating IX where prefix already exists elsewhere #718 * Fix graceful restore of soft-deleted objects with translation active (#580) * Don't return any POC data with status=deleted #569 Hard delete soft-deleted pocs after grace period #566 * django-peeringdb from github@2.0.0.2-beta Co-authored-by: Stefan Pratter <stefan@20c.com>
2020-06-24 12:55:01 -05:00
# these app labels control permissions for the views
# currently exposed in admin
2018-11-08 19:45:21 +00:00
June updates (#751) * Add pointer from API docs to tutorial #650 * Sorting by clicking table headers should use local-compare #356 * Mark IXP peering LAN as bogon #352 * Add help text to "Add (Facility, Network, Exchange)" tab #669 * Add Looking Glass field to the IX object #672 * Add read-only Superuser #679 * Make spelling of traffic levels consistent #519 (#723) * Offer 2FA (#290) * Show "Last Updated" fields on fac, ix, org records (#526) * Enable sort and reverse sort of IP column in IX display (#72) * IRR validation not handling unexpected characters gracefully (#712) * Support alternative direction of writing, e.g. Arabic (#618) * Undeleting an ixlan with an emtpy IPv4 XOR IPv6 field throws a silly error (#644) * Changing org while adding net results in 500 #654 * missing delete button for organisations (#121) * When changing owner of an ix admin GUI borks because of "Ixlan for exchange already exists" #666 * Selection should only present undeleted objects (#664) * change default encoding of API calls to 'utf-8' #663 * Posting https://www.peeringdb.com onto social media doesn't select a good preview image #537 * Revert "Add Looking Glass field to the IX object #672" This reverts commit 4daf2520043c241fabe9a521757efa86a274e28a. Conflicts: peeringdb_server/migrations/0037_ix_looking_glass.py peeringdb_server/views.py * 500 Internal Error when creating IX where prefix already exists elsewhere #718 * Fix graceful restore of soft-deleted objects with translation active (#580) * Don't return any POC data with status=deleted #569 Hard delete soft-deleted pocs after grace period #566 * django-peeringdb from github@2.0.0.2-beta Co-authored-by: Stefan Pratter <stefan@20c.com>
2020-06-24 12:55:01 -05:00
PERMISSION_APP_LABELS = [
"peeringdb_server",
"socialaccount",
"sites",
"auth",
"account",
"oauth2_provider"
]
2018-11-08 19:45:21 +00:00
class StatusFilter(admin.SimpleListFilter):
"""
A listing filter that by default will only show entities
with status="ok"
"""
title = _("Status")
parameter_name = "status"
dflt = "all"
2018-11-08 19:45:21 +00:00
def lookups(self, request, model_admin):
2019-12-05 16:57:52 +00:00
return [
("ok", "ok"),
("pending", "pending"),
("deleted", "deleted"),
("all", "all"),
]
2018-11-08 19:45:21 +00:00
def choices(self, cl):
val = self.value()
if val is None:
val = "all"
2018-11-08 19:45:21 +00:00
for lookup, title in self.lookup_choices:
yield {
2019-12-05 16:57:52 +00:00
"selected": val == lookup,
"query_string": cl.get_query_string({self.parameter_name: lookup}, []),
"display": title,
2018-11-08 19:45:21 +00:00
}
def queryset(self, request, queryset):
if self.value() is None or self.value() == "all":
2018-11-08 19:45:21 +00:00
return queryset.all()
return queryset.filter(**{self.parameter_name: self.value()})
def fk_handleref_filter(form, field, tag=None):
"""
This filters foreign key dropdowns that hold handleref objects
to only contain undeleted objects and the object the instance is currently
set to
"""
if tag is None:
tag = field
if tag in REFTAG_MAP and form.instance:
model = REFTAG_MAP.get(tag)
qset = model.handleref.filter(
2019-12-05 16:57:52 +00:00
Q(status="ok") | Q(id=getattr(form.instance, "%s_id" % field))
)
2018-11-08 19:45:21 +00:00
try:
qset = qset.order_by("name")
except Exception:
pass
June updates (#751) * Add pointer from API docs to tutorial #650 * Sorting by clicking table headers should use local-compare #356 * Mark IXP peering LAN as bogon #352 * Add help text to "Add (Facility, Network, Exchange)" tab #669 * Add Looking Glass field to the IX object #672 * Add read-only Superuser #679 * Make spelling of traffic levels consistent #519 (#723) * Offer 2FA (#290) * Show "Last Updated" fields on fac, ix, org records (#526) * Enable sort and reverse sort of IP column in IX display (#72) * IRR validation not handling unexpected characters gracefully (#712) * Support alternative direction of writing, e.g. Arabic (#618) * Undeleting an ixlan with an emtpy IPv4 XOR IPv6 field throws a silly error (#644) * Changing org while adding net results in 500 #654 * missing delete button for organisations (#121) * When changing owner of an ix admin GUI borks because of "Ixlan for exchange already exists" #666 * Selection should only present undeleted objects (#664) * change default encoding of API calls to 'utf-8' #663 * Posting https://www.peeringdb.com onto social media doesn't select a good preview image #537 * Revert "Add Looking Glass field to the IX object #672" This reverts commit 4daf2520043c241fabe9a521757efa86a274e28a. Conflicts: peeringdb_server/migrations/0037_ix_looking_glass.py peeringdb_server/views.py * 500 Internal Error when creating IX where prefix already exists elsewhere #718 * Fix graceful restore of soft-deleted objects with translation active (#580) * Don't return any POC data with status=deleted #569 Hard delete soft-deleted pocs after grace period #566 * django-peeringdb from github@2.0.0.2-beta Co-authored-by: Stefan Pratter <stefan@20c.com>
2020-06-24 12:55:01 -05:00
if field in form.fields:
form.fields[field].queryset = qset
2018-11-08 19:45:21 +00:00
###############################################################################
@reversion.create_revision()
def merge_organizations(targets, target, request):
"""
Merge organizations specified in targets into organization specified
in target
Arguments:
targets <QuerySet|list> iterable of Organization instances
target <Organization> merge organizations with this organization
"""
if request.user:
reversion.set_user(request.user)
# preare stats
ix_moved = 0
fac_moved = 0
net_moved = 0
user_moved = 0
org_merged = 0
for org in targets:
if org == target:
2019-12-05 16:57:52 +00:00
raise ValueError(_("Target org cannot be in selected organizations list"))
2018-11-08 19:45:21 +00:00
for org in targets:
merge = OrganizationMerge.objects.create(from_org=org, to_org=target)
source_admins = []
# move entities
for ix in org.ix_set.all():
ix.org = target
ix.save()
merge.log_entity(ix)
ix_moved += 1
for net in org.net_set.all():
net.org = target
net.save()
merge.log_entity(net)
net_moved += 1
for fac in org.fac_set.all():
fac.org = target
fac.save()
merge.log_entity(fac)
fac_moved += 1
# move users
for user in org.usergroup.user_set.all():
target.usergroup.user_set.add(user)
org.usergroup.user_set.remove(user)
merge.log_entity(user, note="usergroup")
user_moved += 1
for user in org.admin_usergroup.user_set.all():
target.usergroup.user_set.add(user)
org.admin_usergroup.user_set.remove(user)
merge.log_entity(user, note="admin_usergroup")
user_moved += 1
source_admins.append(user)
# mark deleted
org.delete()
org_merged += 1
2019-12-05 16:57:52 +00:00
mail_users_entity_merge(
source_admins, target.admin_usergroup.user_set.all(), org, target
)
2018-11-08 19:45:21 +00:00
return {
"ix": ix_moved,
"fac": fac_moved,
"net": net_moved,
"user": user_moved,
2019-12-05 16:57:52 +00:00
"org": org_merged,
2018-11-08 19:45:21 +00:00
}
###############################################################################
class StatusForm(baseForms.ModelForm):
2019-12-05 16:57:52 +00:00
status = baseForms.ChoiceField(
choices=[("ok", "ok"), ("pending", "pending"), ("deleted", "deleted")]
)
2018-11-08 19:45:21 +00:00
def __init__(self, *args, **kwargs):
super(StatusForm, self).__init__(*args, **kwargs)
if "instance" in kwargs and kwargs.get("instance"):
inst = kwargs.get("instance")
if inst.status == "ok":
self.fields["status"].choices = [("ok", "ok")]
elif inst.status == "pending":
2019-12-05 16:57:52 +00:00
self.fields["status"].choices = [("ok", "ok"), ("pending", "pending")]
2018-11-08 19:45:21 +00:00
elif inst.status == "deleted":
2019-12-05 16:57:52 +00:00
self.fields["status"].choices = [("ok", "ok"), ("deleted", "deleted")]
2018-11-08 19:45:21 +00:00
class ModelAdminWithUrlActions(admin.ModelAdmin):
def make_redirect(self, obj, action):
opts = obj.model._meta
2019-12-05 16:57:52 +00:00
return redirect("admin:%s_%s_changelist" % (opts.app_label, opts.model_name))
2018-11-08 19:45:21 +00:00
def actions_view(self, request, object_id, action, **kwargs):
"""
this view allows us to call any actions we define in this model admin
to be called via an admin view placed at <model_name>/<id>/<action>/<action_name>
"""
if not request.user.is_superuser:
return HttpResponseForbidden(request)
obj = self.get_queryset(request).filter(pk=object_id)
if obj.exists():
redir = self.make_redirect(obj, action)
action = self.get_action(action)
if action:
action[0](self, request, obj)
return redir
# return redirect("admin:%s_%s_changelist" % (opts.app_label, opts.model_name))
return redirect(
2019-12-05 16:57:52 +00:00
"admin:%s_%s_changelist"
% (obj.model._meta.app_label, obj.model._meta.model_name)
)
2018-11-08 19:45:21 +00:00
def get_urls(self):
"""
add the actions view as a subview of this model's admin views
"""
info = self.model._meta.app_label, self.model._meta.model_name
urls = [
2019-12-05 16:57:52 +00:00
url(
r"^(\d+)/action/([\w]+)/$",
2018-11-08 19:45:21 +00:00
self.admin_site.admin_view(self.actions_view),
2019-12-05 16:57:52 +00:00
name="%s_%s_actions" % info,
),
2018-11-08 19:45:21 +00:00
] + super(ModelAdminWithUrlActions, self).get_urls()
return urls
@reversion.create_revision()
def rollback(modeladmin, request, queryset):
if request.user:
reversion.set_user(request.user)
for row in queryset:
row.rollback()
rollback.short_description = _("ROLLBACK")
@reversion.create_revision()
def soft_delete(modeladmin, request, queryset):
if request.user:
reversion.set_user(request.user)
if queryset.model.handleref.tag == "ixlan":
messages.error(
request,
_(
"Ixlans can no longer be directly deleted as they are now synced to the parent exchange"
),
)
return
2018-11-08 19:45:21 +00:00
for row in queryset:
row.delete()
soft_delete.short_description = _("SOFT DELETE")
class SanitizedAdmin(object):
def get_readonly_fields(self, request, obj=None):
2019-12-05 16:57:52 +00:00
return ("version",) + tuple(
super(SanitizedAdmin, self).get_readonly_fields(request, obj=obj)
)
2018-11-08 19:45:21 +00:00
2019-12-05 16:57:52 +00:00
class SoftDeleteAdmin(
SanitizedAdmin, HandleRefVersionAdmin, VersionAdmin, admin.ModelAdmin
):
2018-11-08 19:45:21 +00:00
"""
Soft delete admin
"""
2019-12-05 16:57:52 +00:00
2018-11-08 19:45:21 +00:00
actions = [soft_delete]
object_history_template = "handleref/grappelli/object_history.html"
version_details_template = "handleref/grappelli/version_details.html"
version_revert_template = "handleref/grappelli/version_revert.html"
version_rollback_template = "handleref/grappelli/version_rollback.html"
2018-11-08 19:45:21 +00:00
def has_delete_permission(self, request, obj=None):
return False
@reversion.create_revision()
def save_formset(self, request, form, formset, change):
if request.user:
reversion.set_user(request.user)
2019-12-05 16:57:52 +00:00
super(SoftDeleteAdmin, self).save_formset(request, form, formset, change)
2018-11-08 19:45:21 +00:00
class ModelAdminWithVQCtrl(object):
"""
Extend from this model admin if you want to add verification queue
approve | deny controls to the top of its form
"""
def get_fieldsets(self, request, obj=None):
"""
we override get_fieldsets so we can attach the vq controls
to the top of the existing fieldset - whethers it's manually or automatically
defined
"""
fieldsets = tuple(
2019-12-05 16:57:52 +00:00
super(ModelAdminWithVQCtrl, self).get_fieldsets(request, obj=obj)
)
2018-11-08 19:45:21 +00:00
# on automatically defined fieldsets it will insert the controls
# somewhere towards the bottom, we dont want that - so we look for it and
# remove it
for k, s in fieldsets:
2019-12-05 16:57:52 +00:00
if "verification_queue" in s["fields"]:
2018-11-08 19:45:21 +00:00
s["fields"].remove("verification_queue")
# attach controls to top of fieldset
2019-12-05 16:57:52 +00:00
fieldsets = (
(None, {"classes": ("wide,"), "fields": ("verification_queue",)}),
) + fieldsets
2018-11-08 19:45:21 +00:00
return fieldsets
def get_readonly_fields(self, request, obj=None):
"""
make the modeladmin aware that "verification_queue" is a valid
readonly field
"""
2019-12-05 16:57:52 +00:00
return ("verification_queue",) + tuple(
super(ModelAdminWithVQCtrl, self).get_readonly_fields(request, obj=obj)
)
2018-11-08 19:45:21 +00:00
def verification_queue(self, obj):
"""
This renders the controls or a status message
"""
2019-01-04 10:02:28 +00:00
if getattr(settings, "DISABLE_VERIFICATION_QUEUE", False):
2018-11-08 19:45:21 +00:00
return _("Verification Queue is currently disabled")
if self.model not in QUEUE_ENABLED:
2019-12-05 16:57:52 +00:00
return _("Verification Queue is currently disabled for this object type")
2018-11-08 19:45:21 +00:00
vq = VerificationQueueItem.objects.filter(
2019-12-05 16:57:52 +00:00
content_type=ContentType.objects.get_for_model(type(obj)), object_id=obj.id
).first()
2018-11-08 19:45:21 +00:00
if vq:
return mark_safe(
'<a class="grp-button" href="{}">{}</a> &nbsp; &nbsp; <a class="grp-button grp-delete-link" href="{}">{}</a>'.format(
vq.approve_admin_url, _("APPROVE"), vq.deny_admin_url, _("DENY")
)
)
2018-11-08 19:45:21 +00:00
return _("APPROVED")
class IXLanPrefixInline(SanitizedAdmin, admin.TabularInline):
model = IXLanPrefix
extra = 0
form = StatusForm
class IXLanInline(SanitizedAdmin, admin.StackedInline):
model = IXLan
extra = 0
form = StatusForm
exclude = ["arp_sponge"]
June updates (#751) * Add pointer from API docs to tutorial #650 * Sorting by clicking table headers should use local-compare #356 * Mark IXP peering LAN as bogon #352 * Add help text to "Add (Facility, Network, Exchange)" tab #669 * Add Looking Glass field to the IX object #672 * Add read-only Superuser #679 * Make spelling of traffic levels consistent #519 (#723) * Offer 2FA (#290) * Show "Last Updated" fields on fac, ix, org records (#526) * Enable sort and reverse sort of IP column in IX display (#72) * IRR validation not handling unexpected characters gracefully (#712) * Support alternative direction of writing, e.g. Arabic (#618) * Undeleting an ixlan with an emtpy IPv4 XOR IPv6 field throws a silly error (#644) * Changing org while adding net results in 500 #654 * missing delete button for organisations (#121) * When changing owner of an ix admin GUI borks because of "Ixlan for exchange already exists" #666 * Selection should only present undeleted objects (#664) * change default encoding of API calls to 'utf-8' #663 * Posting https://www.peeringdb.com onto social media doesn't select a good preview image #537 * Revert "Add Looking Glass field to the IX object #672" This reverts commit 4daf2520043c241fabe9a521757efa86a274e28a. Conflicts: peeringdb_server/migrations/0037_ix_looking_glass.py peeringdb_server/views.py * 500 Internal Error when creating IX where prefix already exists elsewhere #718 * Fix graceful restore of soft-deleted objects with translation active (#580) * Don't return any POC data with status=deleted #569 Hard delete soft-deleted pocs after grace period #566 * django-peeringdb from github@2.0.0.2-beta Co-authored-by: Stefan Pratter <stefan@20c.com>
2020-06-24 12:55:01 -05:00
readonly_fields = ["ixf_import_attempt_info", "prefixes"]
def has_add_permission(self, request):
return False
def has_delete_permission(self, request, obj):
return False
2018-11-08 19:45:21 +00:00
def ixf_import_attempt_info(self, obj):
if obj.ixf_import_attempt:
return mark_safe("<pre>{}</pre>".format(obj.ixf_import_attempt.info))
2018-11-08 19:45:21 +00:00
return ""
def prefixes(self, obj):
return ", ".join(
2019-12-05 16:57:52 +00:00
[str(ixpfx.prefix) for ixpfx in obj.ixpfx_set_active_or_pending]
)
2018-11-08 19:45:21 +00:00
class InternetExchangeFacilityInline(SanitizedAdmin, admin.TabularInline):
model = InternetExchangeFacility
extra = 0
raw_id_fields = ("ix", "facility")
form = StatusForm
autocomplete_lookup_fields = {
"fk": ["facility",],
}
2018-11-08 19:45:21 +00:00
class NetworkContactInline(SanitizedAdmin, admin.TabularInline):
model = NetworkContact
extra = 0
form = StatusForm
class NetworkFacilityInline(SanitizedAdmin, admin.TabularInline):
model = NetworkFacility
extra = 0
raw_id_fields = (
"facility",
"network",
)
form = StatusForm
raw_id_fields = ("facility",)
autocomplete_lookup_fields = {
"fk": ["facility",],
}
2018-11-08 19:45:21 +00:00
class NetworkInternetExchangeInline(SanitizedAdmin, admin.TabularInline):
model = NetworkIXLan
extra = 0
raw_id_fields = ("ixlan", "network")
June updates (#751) * Add pointer from API docs to tutorial #650 * Sorting by clicking table headers should use local-compare #356 * Mark IXP peering LAN as bogon #352 * Add help text to "Add (Facility, Network, Exchange)" tab #669 * Add Looking Glass field to the IX object #672 * Add read-only Superuser #679 * Make spelling of traffic levels consistent #519 (#723) * Offer 2FA (#290) * Show "Last Updated" fields on fac, ix, org records (#526) * Enable sort and reverse sort of IP column in IX display (#72) * IRR validation not handling unexpected characters gracefully (#712) * Support alternative direction of writing, e.g. Arabic (#618) * Undeleting an ixlan with an emtpy IPv4 XOR IPv6 field throws a silly error (#644) * Changing org while adding net results in 500 #654 * missing delete button for organisations (#121) * When changing owner of an ix admin GUI borks because of "Ixlan for exchange already exists" #666 * Selection should only present undeleted objects (#664) * change default encoding of API calls to 'utf-8' #663 * Posting https://www.peeringdb.com onto social media doesn't select a good preview image #537 * Revert "Add Looking Glass field to the IX object #672" This reverts commit 4daf2520043c241fabe9a521757efa86a274e28a. Conflicts: peeringdb_server/migrations/0037_ix_looking_glass.py peeringdb_server/views.py * 500 Internal Error when creating IX where prefix already exists elsewhere #718 * Fix graceful restore of soft-deleted objects with translation active (#580) * Don't return any POC data with status=deleted #569 Hard delete soft-deleted pocs after grace period #566 * django-peeringdb from github@2.0.0.2-beta Co-authored-by: Stefan Pratter <stefan@20c.com>
2020-06-24 12:55:01 -05:00
form = StatusForm
2018-11-08 19:45:21 +00:00
class UserOrgAffiliationRequestInlineForm(baseForms.ModelForm):
def clean(self):
super(UserOrgAffiliationRequestInlineForm, self).clean()
try:
asn = self.cleaned_data.get("asn")
if asn:
rdap_valid = RdapLookup().get_asn(asn).emails
2018-11-08 19:45:21 +00:00
except RdapException as exc:
raise ValidationError({"asn": str(exc)})
class UserOrgAffiliationRequestInline(admin.TabularInline):
model = UserOrgAffiliationRequest
extra = 0
form = UserOrgAffiliationRequestInlineForm
2019-12-05 16:57:52 +00:00
verbose_name_plural = _("User is looking to be affiliated to these Organizations")
raw_id_fields = ("org",)
autocomplete_lookup_fields = {
"fk": ["org"],
}
2018-11-08 19:45:21 +00:00
class InternetExchangeAdminForm(StatusForm):
def __init__(self, *args, **kwargs):
super(InternetExchangeAdminForm, self).__init__(*args, **kwargs)
fk_handleref_filter(self, "org")
class InternetExchangeAdmin(ModelAdminWithVQCtrl, SoftDeleteAdmin):
2019-12-05 16:57:52 +00:00
list_display = (
"name",
"name_long",
"city",
"country",
"status",
"created",
"updated",
)
ordering = ("-created",)
list_filter = (StatusFilter,)
search_fields = ("name",)
readonly_fields = ("id", "nsp_namespace", "ixf_import_history")
2018-11-08 19:45:21 +00:00
inlines = (InternetExchangeFacilityInline, IXLanInline)
form = InternetExchangeAdminForm
raw_id_fields = ("org",)
autocomplete_lookup_fields = {
"fk": ["org"],
}
2018-11-08 19:45:21 +00:00
def ixf_import_history(self, obj):
return mark_safe(
'<a href="{}?q={}">{}</a>'.format(
django.urls.reverse(
"admin:peeringdb_server_ixlanixfmemberimportlog_changelist"
),
obj.id,
_("IXF Import History"),
)
2019-12-05 16:57:52 +00:00
)
2018-11-08 19:45:21 +00:00
class IXLanAdminForm(StatusForm):
def __init__(self, *args, **kwargs):
super(IXLanAdminForm, self).__init__(*args, **kwargs)
fk_handleref_filter(self, "ix")
class IXLanAdmin(SoftDeleteAdmin):
actions = []
2019-12-05 16:57:52 +00:00
list_display = ("ix", "name", "descr", "status")
search_fields = ("name", "ix__name")
2019-12-05 16:57:52 +00:00
list_filter = (StatusFilter,)
readonly_fields = ("id",)
2018-11-08 19:45:21 +00:00
inlines = (IXLanPrefixInline, NetworkInternetExchangeInline)
form = IXLanAdminForm
raw_id_fields = ("ix",)
autocomplete_lookup_fields = {
"fk": ["ix",],
}
2018-11-08 19:45:21 +00:00
class IXLanIXFMemberImportLogEntryInline(admin.TabularInline):
model = IXLanIXFMemberImportLogEntry
2019-12-05 16:57:52 +00:00
fields = (
"netixlan",
"ipv4",
"ipv6",
"asn",
"changes",
"rollback_status",
"action",
"reason",
)
readonly_fields = (
"netixlan",
"ipv4",
"ipv6",
"asn",
"changes",
"rollback_status",
"action",
"reason",
)
raw_id_fields = ("netixlan",)
2018-11-08 19:45:21 +00:00
extra = 0
def has_delete_permission(self, request, obj=None):
return False
def has_add_permission(self, request, obj=None):
return False
def ipv4(self, obj):
return obj.netixlan.ipaddr4 or ""
def ipv6(self, obj):
return obj.netixlan.ipaddr6 or ""
def asn(self, obj):
return obj.netixlan.asn
def changes(self, obj):
vb = obj.version_before
va = obj.version_after
if not vb:
return _("Initial creation of netixlan")
rv = {}
for k, v in list(va.field_dict.items()):
2018-11-08 19:45:21 +00:00
if k in ["created", "updated", "version"]:
continue
v2 = vb.field_dict.get(k)
if v != v2:
if isinstance(v, ipaddress.IPv4Address) or isinstance(
2019-12-05 16:57:52 +00:00
v, ipaddress.IPv6Address
):
2018-11-08 19:45:21 +00:00
rv[k] = str(v)
else:
rv[k] = v
return json.dumps(rv)
def rollback_status(self, obj):
rs = obj.rollback_status()
text = ""
color = ""
if rs == 0:
text = _("CAN BE ROLLED BACK")
color = "#e5f3d6"
elif rs == 1:
text = ("{}<br><small>{}</small>").format(
2019-12-05 16:57:52 +00:00
_("CANNOT BE ROLLED BACK"), _("Has been changed since")
)
2018-11-08 19:45:21 +00:00
color = "#f3ded6"
elif rs == 2:
text = ("{}<br><small>{}</small>").format(
2018-11-08 19:45:21 +00:00
_("CANNOT BE ROLLED BACK"),
2019-12-05 16:57:52 +00:00
_("Netixlan with conflicting ipaddress now exists elsewhere"),
)
2018-11-08 19:45:21 +00:00
color = "#f3ded6"
elif rs == -1:
text = _("HAS BEEN ROLLED BACK")
color = "#d6f0f3"
return mark_safe(
'<div style="background-color:{}">{}</div>'.format(color, text)
)
2018-11-08 19:45:21 +00:00
class IXLanIXFMemberImportLogAdmin(admin.ModelAdmin):
2019-12-05 16:57:52 +00:00
search_fields = ("ixlan__ix__id",)
2018-11-08 19:45:21 +00:00
list_display = ("id", "ix", "ixlan_name", "source", "created", "changes")
readonly_fields = ("ix", "ixlan_name", "source", "changes")
2019-12-05 16:57:52 +00:00
inlines = (IXLanIXFMemberImportLogEntryInline,)
2018-11-08 19:45:21 +00:00
actions = [rollback]
def has_delete_permission(self, request, obj=None):
return False
def changes(self, obj):
return obj.entries.count()
def ix(self, obj):
return mark_safe(
'<a href="{}">{} (ID: {})</a>'.format(
django.urls.reverse(
"admin:peeringdb_server_internetexchange_change",
args=(obj.ixlan.ix.id,),
),
obj.ixlan.ix.name,
obj.ixlan.ix.id,
)
2019-12-05 16:57:52 +00:00
)
2018-11-08 19:45:21 +00:00
def ixlan_name(self, obj):
return mark_safe(
'<a href="{}">{} (ID: {})</a>'.format(
django.urls.reverse(
"admin:peeringdb_server_ixlan_change", args=(obj.ixlan.id,)
),
obj.ixlan.name or "",
obj.ixlan.id,
)
2019-12-05 16:57:52 +00:00
)
2018-11-08 19:45:21 +00:00
def source(self, obj):
return obj.ixlan.ixf_ixp_member_list_url
2019-12-05 16:57:52 +00:00
class SponsorshipOrganizationInline(admin.TabularInline):
model = SponsorshipOrganization
extra = 1
2019-12-05 16:57:52 +00:00
raw_id_fields = ("org",)
autocomplete_lookup_fields = {
2019-12-05 16:57:52 +00:00
"fk": ["org"],
}
2018-11-08 19:45:21 +00:00
2019-12-05 16:57:52 +00:00
2018-11-08 19:45:21 +00:00
class SponsorshipAdmin(admin.ModelAdmin):
2019-12-05 16:57:52 +00:00
list_display = ("organizations", "start_date", "end_date", "level", "status")
readonly_fields = ("organizations", "status", "notify_date")
inlines = (SponsorshipOrganizationInline,)
2018-11-08 19:45:21 +00:00
2019-12-05 16:57:52 +00:00
raw_id_fields = ("orgs",)
2018-11-08 19:45:21 +00:00
autocomplete_lookup_fields = {
2019-12-05 16:57:52 +00:00
"m2m": ["orgs"],
}
2018-11-08 19:45:21 +00:00
def status(self, obj):
now = datetime.datetime.now().replace(tzinfo=UTC())
if not obj.start_date or not obj.end_date:
return _("Not Set")
if obj.start_date <= now and obj.end_date >= now:
for row in obj.sponsorshiporg_set.all():
if row.logo:
return _("Active")
return _("Logo Missing")
2018-11-08 19:45:21 +00:00
elif now > obj.end_date:
return _("Over")
else:
return _("Waiting")
def organizations(self, obj):
qset = obj.orgs.all().order_by("name")
if not qset.count():
return _("No organization(s) set")
return mark_safe("<br>\n".join([html.escape(org.name) for org in qset]))
2019-12-05 16:57:52 +00:00
2018-11-08 19:45:21 +00:00
class PartnershipAdminForm(baseForms.ModelForm):
def __init__(self, *args, **kwargs):
super(PartnershipAdminForm, self).__init__(*args, **kwargs)
fk_handleref_filter(self, "org")
class PartnershipAdmin(admin.ModelAdmin):
2019-12-05 16:57:52 +00:00
list_display = ("org_name", "level", "status")
readonly_fields = ("status", "org_name")
2018-11-08 19:45:21 +00:00
form = PartnershipAdminForm
raw_id_fields = ("org",)
autocomplete_lookup_fields = {
"fk": ["org"],
}
2018-11-08 19:45:21 +00:00
def org_name(self, obj):
if not obj.org:
return ""
return obj.org.name
org_name.admin_order_field = "org__name"
org_name.short_description = "Organization"
def status(self, obj):
if not obj.logo:
return _("Logo Missing")
return _("Active")
class OrganizationAdmin(ModelAdminWithVQCtrl, SoftDeleteAdmin):
2019-12-05 16:57:52 +00:00
list_display = ("handle", "name", "status", "created", "updated")
ordering = ("-created",)
search_fields = ("name",)
list_filter = (StatusFilter,)
readonly_fields = ("id", "nsp_namespace")
2018-11-08 19:45:21 +00:00
form = StatusForm
def get_urls(self):
urls = super(OrganizationAdmin, self).get_urls()
my_urls = [
2019-12-05 16:57:52 +00:00
url(r"^org-merge-tool/merge$", self.org_merge_tool_merge_action),
url(r"^org-merge-tool/$", self.org_merge_tool_view),
2018-11-08 19:45:21 +00:00
]
return my_urls + urls
def org_merge_tool_merge_action(self, request):
if not request.user.is_superuser:
return HttpResponseForbidden(request)
try:
2019-12-05 16:57:52 +00:00
orgs = Organization.objects.filter(id__in=request.GET.get("ids").split(","))
2018-11-08 19:45:21 +00:00
except ValueError:
2019-12-05 16:57:52 +00:00
return JsonResponse({"error": _("Malformed organization ids")}, status=400)
2018-11-08 19:45:21 +00:00
try:
org = Organization.objects.get(id=request.GET.get("id"))
except Organization.DoesNotExist:
2019-12-05 16:57:52 +00:00
return JsonResponse(
{"error": _("Merge target organization does not exist")}, status=400
)
2018-11-08 19:45:21 +00:00
rv = merge_organizations(orgs, org, request)
return JsonResponse(rv)
def org_merge_tool_view(self, request):
if not request.user.is_superuser:
June updates (#751) * Add pointer from API docs to tutorial #650 * Sorting by clicking table headers should use local-compare #356 * Mark IXP peering LAN as bogon #352 * Add help text to "Add (Facility, Network, Exchange)" tab #669 * Add Looking Glass field to the IX object #672 * Add read-only Superuser #679 * Make spelling of traffic levels consistent #519 (#723) * Offer 2FA (#290) * Show "Last Updated" fields on fac, ix, org records (#526) * Enable sort and reverse sort of IP column in IX display (#72) * IRR validation not handling unexpected characters gracefully (#712) * Support alternative direction of writing, e.g. Arabic (#618) * Undeleting an ixlan with an emtpy IPv4 XOR IPv6 field throws a silly error (#644) * Changing org while adding net results in 500 #654 * missing delete button for organisations (#121) * When changing owner of an ix admin GUI borks because of "Ixlan for exchange already exists" #666 * Selection should only present undeleted objects (#664) * change default encoding of API calls to 'utf-8' #663 * Posting https://www.peeringdb.com onto social media doesn't select a good preview image #537 * Revert "Add Looking Glass field to the IX object #672" This reverts commit 4daf2520043c241fabe9a521757efa86a274e28a. Conflicts: peeringdb_server/migrations/0037_ix_looking_glass.py peeringdb_server/views.py * 500 Internal Error when creating IX where prefix already exists elsewhere #718 * Fix graceful restore of soft-deleted objects with translation active (#580) * Don't return any POC data with status=deleted #569 Hard delete soft-deleted pocs after grace period #566 * django-peeringdb from github@2.0.0.2-beta Co-authored-by: Stefan Pratter <stefan@20c.com>
2020-06-24 12:55:01 -05:00
return HttpResponseForbidden()
2018-11-08 19:45:21 +00:00
context = dict(
self.admin_site.each_context(request),
undo_url=django.urls.reverse(
2019-12-05 16:57:52 +00:00
"admin:peeringdb_server_organizationmerge_changelist"
),
title=_("Organization Merging Tool"),
)
2018-11-08 19:45:21 +00:00
return TemplateResponse(request, "admin/org_merge_tool.html", context)
# inlines = (InternetExchangeFacilityInline,NetworkFacilityInline,)
admin.site.register(Organization, OrganizationAdmin)
class OrganizationMergeEntities(admin.TabularInline):
model = OrganizationMergeEntity
extra = 0
readonly_fields = ("content_type", "object_id", "note")
def has_delete_permission(self, request, obj=None):
return False
class OrganizationMergeLog(ModelAdminWithUrlActions):
2019-12-05 16:57:52 +00:00
list_display = ("id", "from_org", "to_org", "created")
search_fields = ("from_org__name", "to_org__name")
readonly_fields = ("from_org", "to_org", "undo_merge")
inlines = (OrganizationMergeEntities,)
2018-11-08 19:45:21 +00:00
def undo_merge(self, obj):
return mark_safe(
June updates (#751) * Add pointer from API docs to tutorial #650 * Sorting by clicking table headers should use local-compare #356 * Mark IXP peering LAN as bogon #352 * Add help text to "Add (Facility, Network, Exchange)" tab #669 * Add Looking Glass field to the IX object #672 * Add read-only Superuser #679 * Make spelling of traffic levels consistent #519 (#723) * Offer 2FA (#290) * Show "Last Updated" fields on fac, ix, org records (#526) * Enable sort and reverse sort of IP column in IX display (#72) * IRR validation not handling unexpected characters gracefully (#712) * Support alternative direction of writing, e.g. Arabic (#618) * Undeleting an ixlan with an emtpy IPv4 XOR IPv6 field throws a silly error (#644) * Changing org while adding net results in 500 #654 * missing delete button for organisations (#121) * When changing owner of an ix admin GUI borks because of "Ixlan for exchange already exists" #666 * Selection should only present undeleted objects (#664) * change default encoding of API calls to 'utf-8' #663 * Posting https://www.peeringdb.com onto social media doesn't select a good preview image #537 * Revert "Add Looking Glass field to the IX object #672" This reverts commit 4daf2520043c241fabe9a521757efa86a274e28a. Conflicts: peeringdb_server/migrations/0037_ix_looking_glass.py peeringdb_server/views.py * 500 Internal Error when creating IX where prefix already exists elsewhere #718 * Fix graceful restore of soft-deleted objects with translation active (#580) * Don't return any POC data with status=deleted #569 Hard delete soft-deleted pocs after grace period #566 * django-peeringdb from github@2.0.0.2-beta Co-authored-by: Stefan Pratter <stefan@20c.com>
2020-06-24 12:55:01 -05:00
'<a class="grp-button grp-delete-link" href="{}">{}</a>'.format(
django.urls.reverse(
"admin:peeringdb_server_organizationmerge_actions",
args=(obj.id,"undo")
),
_("Undo merge")
)
)
2018-11-08 19:45:21 +00:00
@reversion.create_revision()
def undo(modeladmin, request, queryset):
if request.user:
reversion.set_user(request.user)
for each in queryset:
each.undo()
undo.short_description = _("Undo merge")
June updates (#751) * Add pointer from API docs to tutorial #650 * Sorting by clicking table headers should use local-compare #356 * Mark IXP peering LAN as bogon #352 * Add help text to "Add (Facility, Network, Exchange)" tab #669 * Add Looking Glass field to the IX object #672 * Add read-only Superuser #679 * Make spelling of traffic levels consistent #519 (#723) * Offer 2FA (#290) * Show "Last Updated" fields on fac, ix, org records (#526) * Enable sort and reverse sort of IP column in IX display (#72) * IRR validation not handling unexpected characters gracefully (#712) * Support alternative direction of writing, e.g. Arabic (#618) * Undeleting an ixlan with an emtpy IPv4 XOR IPv6 field throws a silly error (#644) * Changing org while adding net results in 500 #654 * missing delete button for organisations (#121) * When changing owner of an ix admin GUI borks because of "Ixlan for exchange already exists" #666 * Selection should only present undeleted objects (#664) * change default encoding of API calls to 'utf-8' #663 * Posting https://www.peeringdb.com onto social media doesn't select a good preview image #537 * Revert "Add Looking Glass field to the IX object #672" This reverts commit 4daf2520043c241fabe9a521757efa86a274e28a. Conflicts: peeringdb_server/migrations/0037_ix_looking_glass.py peeringdb_server/views.py * 500 Internal Error when creating IX where prefix already exists elsewhere #718 * Fix graceful restore of soft-deleted objects with translation active (#580) * Don't return any POC data with status=deleted #569 Hard delete soft-deleted pocs after grace period #566 * django-peeringdb from github@2.0.0.2-beta Co-authored-by: Stefan Pratter <stefan@20c.com>
2020-06-24 12:55:01 -05:00
undo.allowed_permissions = ('change',)
2018-11-08 19:45:21 +00:00
actions = [undo]
def has_delete_permission(self, request, obj=None):
return False
class FacilityAdminForm(StatusForm):
def __init__(self, *args, **kwargs):
super(FacilityAdminForm, self).__init__(*args, **kwargs)
fk_handleref_filter(self, "org")
class FacilityAdmin(ModelAdminWithVQCtrl, SoftDeleteAdmin):
2019-12-05 16:57:52 +00:00
list_display = ("name", "org", "city", "country", "status", "created", "updated")
ordering = ("-created",)
list_filter = (StatusFilter,)
search_fields = ("name",)
readonly_fields = ("id", "nsp_namespace")
raw_id_fields = ("org",)
autocomplete_lookup_fields = {
"fk": ["org"],
}
2018-11-08 19:45:21 +00:00
form = FacilityAdminForm
inlines = (
InternetExchangeFacilityInline,
NetworkFacilityInline,
)
class NetworkAdminForm(StatusForm):
2019-12-05 16:57:52 +00:00
# set initial values on info_prefixes4 and 6 to 0
# this streamlines the process of adding a network through
# the django admin controlpanel (#289)
info_prefixes4 = baseForms.IntegerField(required=False, initial=0)
info_prefixes6 = baseForms.IntegerField(required=False, initial=0)
2018-11-08 19:45:21 +00:00
def __init__(self, *args, **kwargs):
super(NetworkAdminForm, self).__init__(*args, **kwargs)
fk_handleref_filter(self, "org")
class NetworkAdmin(ModelAdminWithVQCtrl, SoftDeleteAdmin):
2019-12-05 16:57:52 +00:00
list_display = ("name", "asn", "aka", "status", "created", "updated")
ordering = ("-created",)
list_filter = (StatusFilter,)
search_fields = ("name", "asn")
readonly_fields = ("id", "nsp_namespace")
2018-11-08 19:45:21 +00:00
form = NetworkAdminForm
inlines = (
NetworkContactInline,
NetworkFacilityInline,
NetworkInternetExchangeInline,
)
raw_id_fields = ("org",)
autocomplete_lookup_fields = {
"fk": ["org",],
}
class InternetExchangeFacilityAdmin(SoftDeleteAdmin):
2019-12-05 16:57:52 +00:00
list_display = ("id", "ix", "facility", "status", "created", "updated")
search_fields = ("ix__name", "facility__name")
readonly_fields = ("id",)
list_filter = (StatusFilter,)
form = StatusForm
raw_id_fields = ("ix", "facility")
autocomplete_lookup_fields = {
"fk": ["ix", "facility"],
}
class IXLanPrefixAdmin(SoftDeleteAdmin):
2019-12-05 16:57:52 +00:00
list_display = ("id", "prefix", "ixlan", "ix", "status", "created", "updated")
readonly_fields = ("ix", "id")
search_fields = ("ixlan__name", "ixlan__ix__name", "prefix")
list_filter = (StatusFilter,)
form = StatusForm
raw_id_fields = ("ixlan",)
autocomplete_lookup_fields = {
"fk": ["ixlan"],
}
def ix(self, obj):
return obj.ixlan.ix
class NetworkIXLanAdmin(SoftDeleteAdmin):
2019-12-05 16:57:52 +00:00
list_display = (
"id",
"asn",
"net",
"ixlan",
"ix",
"ipaddr4",
"ipaddr6",
"status",
"created",
"updated",
)
search_fields = (
"asn",
"network__asn",
"network__name",
"ixlan__name",
"ixlan__ix__name",
"ipaddr4",
"ipaddr6",
)
readonly_fields = ("id", "ix", "net")
list_filter = (StatusFilter,)
form = StatusForm
raw_id_fields = ("network","ixlan")
autocomplete_lookup_fields = {
"fk": ["network","ixlan"],
}
def ix(self, obj):
return obj.ixlan.ix
def net(self, obj):
return "{} (AS{})".format(obj.network.name, obj.network.asn)
class NetworkContactAdmin(SoftDeleteAdmin):
2019-12-05 16:57:52 +00:00
list_display = (
"id",
"net",
"role",
"name",
"phone",
"email",
"status",
"created",
"updated",
)
search_fields = ("network__asn", "network__name")
readonly_fields = ("id", "net")
list_filter = (StatusFilter,)
form = StatusForm
raw_id_fields = ("network",)
autocomplete_lookup_fields = {
"fk": ["network",],
}
def net(self, obj):
return "{} (AS{})".format(obj.network.name, obj.network.asn)
class NetworkFacilityAdmin(SoftDeleteAdmin):
2019-12-05 16:57:52 +00:00
list_display = ("id", "net", "facility", "status", "created", "updated")
search_fields = ("network__asn", "network__name", "facility__name")
readonly_fields = ("id", "net")
list_filter = (StatusFilter,)
form = StatusForm
raw_id_fields = ("network", "facility")
autocomplete_lookup_fields = {
"fk": ["network", "facility"],
}
def net(self, obj):
return "{} (AS{})".format(obj.network.name, obj.network.asn)
2018-11-08 19:45:21 +00:00
class VerificationQueueAdmin(ModelAdminWithUrlActions):
2019-12-05 16:57:52 +00:00
list_display = ("content_type", "item", "created", "view", "extra")
filter_fields = ("content_type",)
readonly_fields = ("created", "view", "extra")
search_fields = ("item",)
2018-11-08 19:45:21 +00:00
raw_id_fields = ("user",)
autocomplete_lookup_fields = {
"fk": ["user"],
}
2018-11-08 19:45:21 +00:00
def get_search_results(self, request, queryset, search_term):
2019-12-05 16:57:52 +00:00
# queryset, use_distinct = super(VerificationQueueAdmin, self).get_search_results(request, queryset, search_term)
if not search_term or search_term == "":
2018-11-08 19:45:21 +00:00
return queryset, False
use_distinct = True
myset = VerificationQueueItem.objects.none()
for model in QUEUE_ENABLED:
if model == User:
qrs = model.objects.filter(username__icontains=search_term)
else:
qrs = model.objects.filter(name__icontains=search_term)
content_type = ContentType.objects.get_for_model(model)
for instance in list(qrs):
vq = VerificationQueueItem.objects.filter(
2019-12-05 16:57:52 +00:00
content_type=content_type, object_id=instance.id
)
myset |= queryset & vq
2018-11-08 19:45:21 +00:00
return myset, use_distinct
def make_redirect(self, obj, action):
if action == "vq_approve":
opts = type(obj.first().item)._meta
return redirect(
django.urls.reverse(
2019-12-05 16:57:52 +00:00
"admin:%s_%s_change" % (opts.app_label, opts.model_name),
args=(obj.first().item.id,),
)
)
2018-11-08 19:45:21 +00:00
opts = obj.model._meta
2019-12-05 16:57:52 +00:00
return redirect("admin:%s_%s_changelist" % (opts.app_label, opts.model_name))
2018-11-08 19:45:21 +00:00
def vq_approve(self, request, queryset):
with reversion.create_revision():
reversion.set_user(request.user)
for each in queryset:
each.approve()
2018-11-08 19:45:21 +00:00
vq_approve.short_description = _("APPROVE selected items")
June updates (#751) * Add pointer from API docs to tutorial #650 * Sorting by clicking table headers should use local-compare #356 * Mark IXP peering LAN as bogon #352 * Add help text to "Add (Facility, Network, Exchange)" tab #669 * Add Looking Glass field to the IX object #672 * Add read-only Superuser #679 * Make spelling of traffic levels consistent #519 (#723) * Offer 2FA (#290) * Show "Last Updated" fields on fac, ix, org records (#526) * Enable sort and reverse sort of IP column in IX display (#72) * IRR validation not handling unexpected characters gracefully (#712) * Support alternative direction of writing, e.g. Arabic (#618) * Undeleting an ixlan with an emtpy IPv4 XOR IPv6 field throws a silly error (#644) * Changing org while adding net results in 500 #654 * missing delete button for organisations (#121) * When changing owner of an ix admin GUI borks because of "Ixlan for exchange already exists" #666 * Selection should only present undeleted objects (#664) * change default encoding of API calls to 'utf-8' #663 * Posting https://www.peeringdb.com onto social media doesn't select a good preview image #537 * Revert "Add Looking Glass field to the IX object #672" This reverts commit 4daf2520043c241fabe9a521757efa86a274e28a. Conflicts: peeringdb_server/migrations/0037_ix_looking_glass.py peeringdb_server/views.py * 500 Internal Error when creating IX where prefix already exists elsewhere #718 * Fix graceful restore of soft-deleted objects with translation active (#580) * Don't return any POC data with status=deleted #569 Hard delete soft-deleted pocs after grace period #566 * django-peeringdb from github@2.0.0.2-beta Co-authored-by: Stefan Pratter <stefan@20c.com>
2020-06-24 12:55:01 -05:00
vq_approve.allowed_permissions = ('change',)
2018-11-08 19:45:21 +00:00
def vq_deny(modeladmin, request, queryset):
for each in queryset:
each.deny()
vq_deny.short_description = _("DENY and delete selected items")
June updates (#751) * Add pointer from API docs to tutorial #650 * Sorting by clicking table headers should use local-compare #356 * Mark IXP peering LAN as bogon #352 * Add help text to "Add (Facility, Network, Exchange)" tab #669 * Add Looking Glass field to the IX object #672 * Add read-only Superuser #679 * Make spelling of traffic levels consistent #519 (#723) * Offer 2FA (#290) * Show "Last Updated" fields on fac, ix, org records (#526) * Enable sort and reverse sort of IP column in IX display (#72) * IRR validation not handling unexpected characters gracefully (#712) * Support alternative direction of writing, e.g. Arabic (#618) * Undeleting an ixlan with an emtpy IPv4 XOR IPv6 field throws a silly error (#644) * Changing org while adding net results in 500 #654 * missing delete button for organisations (#121) * When changing owner of an ix admin GUI borks because of "Ixlan for exchange already exists" #666 * Selection should only present undeleted objects (#664) * change default encoding of API calls to 'utf-8' #663 * Posting https://www.peeringdb.com onto social media doesn't select a good preview image #537 * Revert "Add Looking Glass field to the IX object #672" This reverts commit 4daf2520043c241fabe9a521757efa86a274e28a. Conflicts: peeringdb_server/migrations/0037_ix_looking_glass.py peeringdb_server/views.py * 500 Internal Error when creating IX where prefix already exists elsewhere #718 * Fix graceful restore of soft-deleted objects with translation active (#580) * Don't return any POC data with status=deleted #569 Hard delete soft-deleted pocs after grace period #566 * django-peeringdb from github@2.0.0.2-beta Co-authored-by: Stefan Pratter <stefan@20c.com>
2020-06-24 12:55:01 -05:00
vq_deny.allowed_permissions = ('change',)
2018-11-08 19:45:21 +00:00
actions = [vq_approve, vq_deny]
def view(self, obj):
return mark_safe('<a href="{}">{}</a>'.format(obj.item_admin_url, _("View")))
2018-11-08 19:45:21 +00:00
def extra(self, obj):
2019-12-05 16:57:52 +00:00
if hasattr(obj.item, "org") and obj.item.org.id == settings.SUGGEST_ENTITY_ORG:
2018-11-08 19:45:21 +00:00
return "Suggestion"
return ""
class UserOrgAffiliationRequestAdmin(ModelAdminWithUrlActions):
2019-12-05 16:57:52 +00:00
list_display = ("user", "asn", "org", "created", "status")
search_fields = ("user", "asn")
readonly_fields = ("created",)
2018-11-08 19:45:21 +00:00
raw_id_fields = ("user", "org")
autocomplete_lookup_fields = {
"fk": ["user", "org"],
}
2018-11-08 19:45:21 +00:00
def approve_and_notify(self, request, queryset):
for each in queryset:
if each.status == "canceled":
messages.error(request, _("Cannot approve a canceled affiliation request"))
continue
2018-11-08 19:45:21 +00:00
each.approve()
each.notify_ownership_approved()
self.message_user(
request, _("Affiliation request was approved and the user was notified.")
)
2018-11-08 19:45:21 +00:00
approve_and_notify.short_description = _("Approve and notify User")
def approve(self, request, queryset):
for each in queryset:
if each.status == "canceled":
messages.error(request, _("Cannot approve a canceled affiliation request"))
continue
2018-11-08 19:45:21 +00:00
each.approve()
approve.short_description = _("Approve")
def deny(self, request, queryset):
for each in queryset:
if each.status == "canceled":
messages.error(request, _("Cannot deny a canceled affiliation request"))
continue
2018-11-08 19:45:21 +00:00
each.deny()
deny.short_description = _("Deny")
def has_delete_permission(self, request, obj=None):
return False
actions = [approve_and_notify, approve, deny]
# need to do this for add via django admin to use the right model
class UserCreationForm(forms.UserCreationForm):
def clean_username(self):
username = self.cleaned_data["username"]
try:
User._default_manager.get(username=username)
except User.DoesNotExist:
return username
raise ValidationError(self.error_messages["duplicate_username"])
class Meta(forms.UserCreationForm.Meta):
model = User
2019-12-05 16:57:52 +00:00
fields = ("username", "password", "email")
2018-11-08 19:45:21 +00:00
class UserAdmin(ModelAdminWithVQCtrl, UserAdmin):
2019-12-05 16:57:52 +00:00
inlines = (UserOrgAffiliationRequestInline,)
readonly_fields = (
"email_status",
"organizations",
"view_permissions",
"change_password",
)
list_display = (
"username",
"email",
"first_name",
"last_name",
"email_status",
"status",
)
2018-11-08 19:45:21 +00:00
add_form = UserCreationForm
2019-12-05 16:57:52 +00:00
add_fieldsets = (
(
None,
{
"classes": ("wide",),
"fields": ("username", "password1", "password2", "email"),
},
),
)
fieldsets = (
((None, {"classes": ("wide",), "fields": ("email_status", "change_password")}),)
+ UserAdmin.fieldsets
+ ((None, {"classes": ("wide",), "fields": ("organizations",)}),)
)
2018-11-08 19:45:21 +00:00
# we want to get rid of user permissions and group editor as that
# will be displayed on a separate page, for performance reasons
for name, grp in fieldsets:
2019-12-05 16:57:52 +00:00
grp["fields"] = tuple(
[
fld
for fld in grp["fields"]
if fld
not in [
"groups",
"user_permissions",
"is_staff",
"is_active",
"is_superuser",
]
2018-11-08 19:45:21 +00:00
]
2019-12-05 16:57:52 +00:00
)
2018-11-08 19:45:21 +00:00
if name == "Permissions":
2019-12-05 16:57:52 +00:00
grp["fields"] += ("view_permissions",)
2018-11-08 19:45:21 +00:00
def version(self, obj):
"""
Users are not versioned, but ModelAdminWithVQCtrl defines
a readonly field called "version", for sake of completion
return a 0 version here
"""
return 0
def change_password(self, obj):
return mark_safe(
'<a href="{}">{}</a>'.format(
django.urls.reverse("admin:auth_user_password_change", args=(obj.id,)),
_("Change Password"),
)
2019-12-05 16:57:52 +00:00
)
2018-11-08 19:45:21 +00:00
def view_permissions(self, obj):
url = django.urls.reverse(
2019-12-05 16:57:52 +00:00
"admin:%s_%s_change"
% (UserPermission._meta.app_label, UserPermission._meta.model_name),
args=(obj.id,),
)
2018-11-08 19:45:21 +00:00
return mark_safe('<a href="{}">{}</a>'.format(url, _("Edit Permissions")))
2018-11-08 19:45:21 +00:00
def email_status(self, obj):
if obj.email_confirmed:
return mark_safe(
'<span style="color:darkgreen">{}</span>'.format(_("VERIFIED"))
)
2018-11-08 19:45:21 +00:00
else:
return mark_safe(
'<span style="color:darkred">{}</span>'.format(_("UNVERIFIED"))
)
2018-11-08 19:45:21 +00:00
def organizations(self, obj):
return mark_safe(
2019-12-05 16:57:52 +00:00
loader.get_template("admin/user-organizations.html")
.render({"organizations": obj.organizations, "user": obj})
.replace("\n", "")
)
2018-11-08 19:45:21 +00:00
class UserPermission(User):
class Meta:
proxy = True
verbose_name = _("User Permission")
verbose_name_plural = _("User Permissions")
class UserPermissionAdmin(UserAdmin):
2019-12-05 16:57:52 +00:00
search_fields = ("username",)
2018-11-08 19:45:21 +00:00
2019-12-05 16:57:52 +00:00
inlines = (
UserOrgAffiliationRequestInline,
UserPermissionInline,
UserPermissionInlineAdd,
)
2018-11-08 19:45:21 +00:00
2019-12-05 16:57:52 +00:00
fieldsets = (
(
None,
{
"fields": (
"user",
"is_active",
"is_staff",
"is_superuser",
"groups",
"user_permissions",
),
"classes": ("wide",),
},
),
)
2018-11-08 19:45:21 +00:00
2019-12-05 16:57:52 +00:00
readonly_fields = ("user",)
2018-11-08 19:45:21 +00:00
def get_form(self, request, obj=None, **kwargs):
# we want to remove the password field from the form
# since we dont send it and dont want to run clean for it
2019-12-05 16:57:52 +00:00
form = super(UserPermissionAdmin, self).get_form(request, obj, **kwargs)
del form.base_fields["password"]
2018-11-08 19:45:21 +00:00
return form
def user(self, obj):
url = django.urls.reverse(
2019-12-05 16:57:52 +00:00
"admin:%s_%s_change" % (User._meta.app_label, User._meta.model_name),
args=(obj.id,),
)
2018-11-08 19:45:21 +00:00
return mark_safe('<a href="%s">%s</a>' % (url, obj.username))
2018-11-08 19:45:21 +00:00
def clean_password(self):
pass
class DuplicateIPNetworkIXLan(NetworkIXLan):
"""
Issue #96, #92
Interface to streamline manual cleanup of duplicate netixlan ips
Can be removed after #92 is fully completed
"""
class Meta(object):
proxy = True
verbose_name = _("Duplicate IP")
verbose_name_plural = _("Duplicate IPs")
class DuplicateIPResultList(list):
def set_qs(self, qs):
self.qs = qs
def filter(self, **kwargs):
return self.qs.filter(**kwargs)
def _clone(self):
return self
class DuplicateIPChangeList(ChangeList):
"""
Issue #96, #92
Interface to streamline manual clean up of duplicate netixlan ips
Can be removed after #92 is fully completed
"""
def get_queryset(self, request):
ip4s = {}
ip6s = {}
qs = super(DuplicateIPChangeList, self).get_queryset(request)
sort_keys = list(qs.query.order_by)
2018-11-08 19:45:21 +00:00
if "pk" in sort_keys:
sort_keys.remove("pk")
if "-pk" in sort_keys:
sort_keys.remove("-pk")
def sorters(item):
v = []
for key in sort_keys:
if key[0] == "-":
key = key[1:]
v.insert(0, -getattr(item, key))
else:
v.insert(0, getattr(item, key))
return v
qs = NetworkIXLan.objects.filter(status="ok")
t = time.time()
for netixlan in qs:
if netixlan.ipaddr4:
ip4 = str(netixlan.ipaddr4)
if ip4 not in ip4s:
ip4s[ip4] = [netixlan]
else:
ip4s[ip4].append(netixlan)
if netixlan.ipaddr6:
ip6 = str(netixlan.ipaddr6)
if ip6 not in ip6s:
ip6s[ip6] = [netixlan]
else:
ip6s[ip6].append(netixlan)
collisions = []
for ip, netixlans in list(ip4s.items()):
2018-11-08 19:45:21 +00:00
if len(netixlans) > 1:
for netixlan in netixlans:
netixlan.ip_dupe = 4
netixlan.exchange = int(netixlan.ixlan.ix_id)
netixlan.ip_sorter = int(netixlan.ipaddr4)
2019-12-05 16:57:52 +00:00
netixlan.dt_sorter = int(netixlan.updated.strftime("%s"))
2018-11-08 19:45:21 +00:00
collisions.append(netixlan)
for ip, netixlans in list(ip6s.items()):
2018-11-08 19:45:21 +00:00
if len(netixlans) > 1:
for netixlan in netixlans:
netixlan.ip_dupe = 6
netixlan.exchange = int(netixlan.ixlan.ix_id)
netixlan.ip_sorter = int(netixlan.ipaddr6)
2019-12-05 16:57:52 +00:00
netixlan.dt_sorter = int(netixlan.updated.strftime("%s"))
2018-11-08 19:45:21 +00:00
collisions.append(netixlan)
if sort_keys != ["pk"] and sort_keys != ["-pk"]:
collisions = sorted(collisions, key=sorters)
collisions = DuplicateIPResultList(collisions)
collisions.set_qs(qs)
return collisions
class DuplicateIPAdmin(SoftDeleteAdmin):
"""
Issue #96, #92
Interface to streamline manual clean up of duplicate netixlan ips
Can be removed after #92 is fully completed
"""
2019-12-05 16:57:52 +00:00
list_display = ("id_ro", "ip", "asn", "ix", "net", "updated_ro", "status_ro")
readonly_fields = ("id_ro", "ip", "net", "ix", "asn", "updated_ro", "status_ro")
June updates (#751) * Add pointer from API docs to tutorial #650 * Sorting by clicking table headers should use local-compare #356 * Mark IXP peering LAN as bogon #352 * Add help text to "Add (Facility, Network, Exchange)" tab #669 * Add Looking Glass field to the IX object #672 * Add read-only Superuser #679 * Make spelling of traffic levels consistent #519 (#723) * Offer 2FA (#290) * Show "Last Updated" fields on fac, ix, org records (#526) * Enable sort and reverse sort of IP column in IX display (#72) * IRR validation not handling unexpected characters gracefully (#712) * Support alternative direction of writing, e.g. Arabic (#618) * Undeleting an ixlan with an emtpy IPv4 XOR IPv6 field throws a silly error (#644) * Changing org while adding net results in 500 #654 * missing delete button for organisations (#121) * When changing owner of an ix admin GUI borks because of "Ixlan for exchange already exists" #666 * Selection should only present undeleted objects (#664) * change default encoding of API calls to 'utf-8' #663 * Posting https://www.peeringdb.com onto social media doesn't select a good preview image #537 * Revert "Add Looking Glass field to the IX object #672" This reverts commit 4daf2520043c241fabe9a521757efa86a274e28a. Conflicts: peeringdb_server/migrations/0037_ix_looking_glass.py peeringdb_server/views.py * 500 Internal Error when creating IX where prefix already exists elsewhere #718 * Fix graceful restore of soft-deleted objects with translation active (#580) * Don't return any POC data with status=deleted #569 Hard delete soft-deleted pocs after grace period #566 * django-peeringdb from github@2.0.0.2-beta Co-authored-by: Stefan Pratter <stefan@20c.com>
2020-06-24 12:55:01 -05:00
form = StatusForm
2018-11-08 19:45:21 +00:00
list_per_page = 1000
2019-12-05 16:57:52 +00:00
fieldsets = (
(
None,
{
"classes": ("wide",),
"fields": (
"status",
"asn",
"ipaddr4",
"ipaddr6",
"ix",
"net",
June updates (#751) * Add pointer from API docs to tutorial #650 * Sorting by clicking table headers should use local-compare #356 * Mark IXP peering LAN as bogon #352 * Add help text to "Add (Facility, Network, Exchange)" tab #669 * Add Looking Glass field to the IX object #672 * Add read-only Superuser #679 * Make spelling of traffic levels consistent #519 (#723) * Offer 2FA (#290) * Show "Last Updated" fields on fac, ix, org records (#526) * Enable sort and reverse sort of IP column in IX display (#72) * IRR validation not handling unexpected characters gracefully (#712) * Support alternative direction of writing, e.g. Arabic (#618) * Undeleting an ixlan with an emtpy IPv4 XOR IPv6 field throws a silly error (#644) * Changing org while adding net results in 500 #654 * missing delete button for organisations (#121) * When changing owner of an ix admin GUI borks because of "Ixlan for exchange already exists" #666 * Selection should only present undeleted objects (#664) * change default encoding of API calls to 'utf-8' #663 * Posting https://www.peeringdb.com onto social media doesn't select a good preview image #537 * Revert "Add Looking Glass field to the IX object #672" This reverts commit 4daf2520043c241fabe9a521757efa86a274e28a. Conflicts: peeringdb_server/migrations/0037_ix_looking_glass.py peeringdb_server/views.py * 500 Internal Error when creating IX where prefix already exists elsewhere #718 * Fix graceful restore of soft-deleted objects with translation active (#580) * Don't return any POC data with status=deleted #569 Hard delete soft-deleted pocs after grace period #566 * django-peeringdb from github@2.0.0.2-beta Co-authored-by: Stefan Pratter <stefan@20c.com>
2020-06-24 12:55:01 -05:00
"updated_ro",
2019-12-05 16:57:52 +00:00
),
},
),
)
2018-11-08 19:45:21 +00:00
def get_changelist(self, request):
return DuplicateIPChangeList
def id_ro(self, obj):
return obj.id
id_ro.admin_order_field = "id"
id_ro.short_description = "Id"
def status_ro(self, obj):
return obj.status
status_ro.admin_order_field = None
status_ro.short_description = _("Status")
def updated_ro(self, obj):
return obj.updated
updated_ro.admin_order_field = "dt_sorter"
updated_ro.short_description = _("Updated")
def ip(self, obj):
if obj.ip_dupe == 4:
return obj.ipaddr4
elif obj.ip_dupe == 6:
return obj.ipaddr6
ip.admin_order_field = "ip_sorter"
def net(self, obj):
return mark_safe('<a href="/net/%d">%d</a>' % (obj.network_id, obj.network_id))
2018-11-08 19:45:21 +00:00
net.admin_order_field = "network_id"
def ix(self, obj):
return mark_safe('<a href="/ix/%d">%d</a>' % (obj.ixlan.ix.id, obj.ixlan.ix.id))
2018-11-08 19:45:21 +00:00
ix.admin_order_field = "exchange"
def changelist_view(self, request, extra_context=None):
2019-12-05 16:57:52 +00:00
extra_context = {"title": "Duplicate IPs"}
2018-11-08 19:45:21 +00:00
return super(DuplicateIPAdmin, self).changelist_view(
2019-12-05 16:57:52 +00:00
request, extra_context=extra_context
)
2018-11-08 19:45:21 +00:00
def has_add_permission(self, request):
return False
## COMMANDLINE TOOL ADMIN
class CommandLineToolPrepareForm(baseForms.Form):
"""
Form that allows user to select which commandline tool
to run
"""
2019-12-05 16:57:52 +00:00
2018-11-08 19:45:21 +00:00
tool = baseForms.ChoiceField(choices=COMMANDLINE_TOOLS)
class CommandLineToolAdmin(admin.ModelAdmin):
"""
View that lets staff users run peeringdb command line tools
"""
2019-12-05 16:57:52 +00:00
2019-01-04 10:02:28 +00:00
list_display = ("tool", "description", "user", "created", "status")
2019-12-05 16:57:52 +00:00
readonly_fields = (
"tool",
"description",
"arguments",
"result",
"user",
"created",
"status",
)
2018-11-08 19:45:21 +00:00
def has_delete_permission(self, request, obj=None):
return False
def get_urls(self):
urls = super(CommandLineToolAdmin, self).get_urls()
my_urls = [
2019-12-05 16:57:52 +00:00
url(
r"^prepare/$",
self.prepare_command_view,
name="peeringdb_server_commandlinetool_prepare",
),
url(
r"^preview/$",
self.preview_command_view,
name="peeringdb_server_commandlinetool_preview",
),
url(
r"^run/$",
self.run_command_view,
name="peeringdb_server_commandlinetool_run",
),
2018-11-08 19:45:21 +00:00
]
return my_urls + urls
def prepare_command_view(self, request):
"""
This view has the user select which command they want to run and
with which arguments.
"""
June updates (#751) * Add pointer from API docs to tutorial #650 * Sorting by clicking table headers should use local-compare #356 * Mark IXP peering LAN as bogon #352 * Add help text to "Add (Facility, Network, Exchange)" tab #669 * Add Looking Glass field to the IX object #672 * Add read-only Superuser #679 * Make spelling of traffic levels consistent #519 (#723) * Offer 2FA (#290) * Show "Last Updated" fields on fac, ix, org records (#526) * Enable sort and reverse sort of IP column in IX display (#72) * IRR validation not handling unexpected characters gracefully (#712) * Support alternative direction of writing, e.g. Arabic (#618) * Undeleting an ixlan with an emtpy IPv4 XOR IPv6 field throws a silly error (#644) * Changing org while adding net results in 500 #654 * missing delete button for organisations (#121) * When changing owner of an ix admin GUI borks because of "Ixlan for exchange already exists" #666 * Selection should only present undeleted objects (#664) * change default encoding of API calls to 'utf-8' #663 * Posting https://www.peeringdb.com onto social media doesn't select a good preview image #537 * Revert "Add Looking Glass field to the IX object #672" This reverts commit 4daf2520043c241fabe9a521757efa86a274e28a. Conflicts: peeringdb_server/migrations/0037_ix_looking_glass.py peeringdb_server/views.py * 500 Internal Error when creating IX where prefix already exists elsewhere #718 * Fix graceful restore of soft-deleted objects with translation active (#580) * Don't return any POC data with status=deleted #569 Hard delete soft-deleted pocs after grace period #566 * django-peeringdb from github@2.0.0.2-beta Co-authored-by: Stefan Pratter <stefan@20c.com>
2020-06-24 12:55:01 -05:00
if not self.has_add_permission(request):
return HttpResponseForbidden()
2018-11-08 19:45:21 +00:00
context = dict(self.admin_site.each_context(request))
title = "Commandline Tools"
action = "prepare"
if request.method == "POST":
form = CommandLineToolPrepareForm(request.POST, request.FILES)
if form.is_valid():
action = "preview"
tool = acltools.get_tool(request.POST.get("tool"), form)
context.update(tool=tool)
title = tool.name
form = tool.form
else:
form = CommandLineToolPrepareForm()
2019-12-05 16:57:52 +00:00
context.update(
{
"adminform": helpers.AdminForm(
form,
list([(None, {"fields": form.base_fields})]),
self.get_prepopulated_fields(request),
),
"action": action,
"app_label": self.model._meta.app_label,
"opts": self.model._meta,
"title": title,
}
)
2018-11-08 19:45:21 +00:00
return TemplateResponse(
request,
"admin/peeringdb_server/commandlinetool/prepare_command.html",
2019-12-05 16:57:52 +00:00
context,
)
2018-11-08 19:45:21 +00:00
def preview_command_view(self, request):
"""
This view has the user preview the result of running the command
"""
June updates (#751) * Add pointer from API docs to tutorial #650 * Sorting by clicking table headers should use local-compare #356 * Mark IXP peering LAN as bogon #352 * Add help text to "Add (Facility, Network, Exchange)" tab #669 * Add Looking Glass field to the IX object #672 * Add read-only Superuser #679 * Make spelling of traffic levels consistent #519 (#723) * Offer 2FA (#290) * Show "Last Updated" fields on fac, ix, org records (#526) * Enable sort and reverse sort of IP column in IX display (#72) * IRR validation not handling unexpected characters gracefully (#712) * Support alternative direction of writing, e.g. Arabic (#618) * Undeleting an ixlan with an emtpy IPv4 XOR IPv6 field throws a silly error (#644) * Changing org while adding net results in 500 #654 * missing delete button for organisations (#121) * When changing owner of an ix admin GUI borks because of "Ixlan for exchange already exists" #666 * Selection should only present undeleted objects (#664) * change default encoding of API calls to 'utf-8' #663 * Posting https://www.peeringdb.com onto social media doesn't select a good preview image #537 * Revert "Add Looking Glass field to the IX object #672" This reverts commit 4daf2520043c241fabe9a521757efa86a274e28a. Conflicts: peeringdb_server/migrations/0037_ix_looking_glass.py peeringdb_server/views.py * 500 Internal Error when creating IX where prefix already exists elsewhere #718 * Fix graceful restore of soft-deleted objects with translation active (#580) * Don't return any POC data with status=deleted #569 Hard delete soft-deleted pocs after grace period #566 * django-peeringdb from github@2.0.0.2-beta Co-authored-by: Stefan Pratter <stefan@20c.com>
2020-06-24 12:55:01 -05:00
if not self.has_add_permission(request):
return HttpResponseForbidden()
2018-11-08 19:45:21 +00:00
context = dict(self.admin_site.each_context(request))
if request.method == "POST":
tool = acltools.get_tool_from_data(request.POST)
context.update(tool=tool)
if tool.form_instance.is_valid():
action = "run"
tool.run(request.user, commit=False)
else:
June updates (#751) * Add pointer from API docs to tutorial #650 * Sorting by clicking table headers should use local-compare #356 * Mark IXP peering LAN as bogon #352 * Add help text to "Add (Facility, Network, Exchange)" tab #669 * Add Looking Glass field to the IX object #672 * Add read-only Superuser #679 * Make spelling of traffic levels consistent #519 (#723) * Offer 2FA (#290) * Show "Last Updated" fields on fac, ix, org records (#526) * Enable sort and reverse sort of IP column in IX display (#72) * IRR validation not handling unexpected characters gracefully (#712) * Support alternative direction of writing, e.g. Arabic (#618) * Undeleting an ixlan with an emtpy IPv4 XOR IPv6 field throws a silly error (#644) * Changing org while adding net results in 500 #654 * missing delete button for organisations (#121) * When changing owner of an ix admin GUI borks because of "Ixlan for exchange already exists" #666 * Selection should only present undeleted objects (#664) * change default encoding of API calls to 'utf-8' #663 * Posting https://www.peeringdb.com onto social media doesn't select a good preview image #537 * Revert "Add Looking Glass field to the IX object #672" This reverts commit 4daf2520043c241fabe9a521757efa86a274e28a. Conflicts: peeringdb_server/migrations/0037_ix_looking_glass.py peeringdb_server/views.py * 500 Internal Error when creating IX where prefix already exists elsewhere #718 * Fix graceful restore of soft-deleted objects with translation active (#580) * Don't return any POC data with status=deleted #569 Hard delete soft-deleted pocs after grace period #566 * django-peeringdb from github@2.0.0.2-beta Co-authored-by: Stefan Pratter <stefan@20c.com>
2020-06-24 12:55:01 -05:00
action = "run"
2018-11-08 19:45:21 +00:00
form = tool.form_instance
else:
raise Exception(_("Only POST requests allowed."))
2019-12-05 16:57:52 +00:00
context.update(
{
"adminform": helpers.AdminForm(
form,
list([(None, {"fields": form.base_fields})]),
self.get_prepopulated_fields(request),
),
"action": action,
"app_label": self.model._meta.app_label,
"opts": self.model._meta,
"title": _("{} (Preview)").format(tool.name),
}
)
2018-11-08 19:45:21 +00:00
return TemplateResponse(
request,
"admin/peeringdb_server/commandlinetool/preview_command.html",
2019-12-05 16:57:52 +00:00
context,
)
2018-11-08 19:45:21 +00:00
def run_command_view(self, request):
"""
This view has the user running the command and commiting changes
to the database.
"""
June updates (#751) * Add pointer from API docs to tutorial #650 * Sorting by clicking table headers should use local-compare #356 * Mark IXP peering LAN as bogon #352 * Add help text to "Add (Facility, Network, Exchange)" tab #669 * Add Looking Glass field to the IX object #672 * Add read-only Superuser #679 * Make spelling of traffic levels consistent #519 (#723) * Offer 2FA (#290) * Show "Last Updated" fields on fac, ix, org records (#526) * Enable sort and reverse sort of IP column in IX display (#72) * IRR validation not handling unexpected characters gracefully (#712) * Support alternative direction of writing, e.g. Arabic (#618) * Undeleting an ixlan with an emtpy IPv4 XOR IPv6 field throws a silly error (#644) * Changing org while adding net results in 500 #654 * missing delete button for organisations (#121) * When changing owner of an ix admin GUI borks because of "Ixlan for exchange already exists" #666 * Selection should only present undeleted objects (#664) * change default encoding of API calls to 'utf-8' #663 * Posting https://www.peeringdb.com onto social media doesn't select a good preview image #537 * Revert "Add Looking Glass field to the IX object #672" This reverts commit 4daf2520043c241fabe9a521757efa86a274e28a. Conflicts: peeringdb_server/migrations/0037_ix_looking_glass.py peeringdb_server/views.py * 500 Internal Error when creating IX where prefix already exists elsewhere #718 * Fix graceful restore of soft-deleted objects with translation active (#580) * Don't return any POC data with status=deleted #569 Hard delete soft-deleted pocs after grace period #566 * django-peeringdb from github@2.0.0.2-beta Co-authored-by: Stefan Pratter <stefan@20c.com>
2020-06-24 12:55:01 -05:00
if not self.has_add_permission(request):
return HttpResponseForbidden()
2018-11-08 19:45:21 +00:00
context = dict(self.admin_site.each_context(request))
if request.method == "POST":
tool = acltools.get_tool_from_data(request.POST)
context.update(tool=tool)
if tool.form_instance.is_valid():
tool.run(request.user, commit=True)
form = tool.form_instance
else:
raise Exception(_("Only POST requests allowed."))
2019-12-05 16:57:52 +00:00
context.update(
{
"adminform": helpers.AdminForm(
form,
list([(None, {"fields": form.base_fields})]),
self.get_prepopulated_fields(request),
),
"action": "run",
"app_label": self.model._meta.app_label,
"opts": self.model._meta,
"title": tool.name,
}
)
2018-11-08 19:45:21 +00:00
return TemplateResponse(
2019-12-05 16:57:52 +00:00
request, "admin/peeringdb_server/commandlinetool/run_command.html", context
)
2018-11-08 19:45:21 +00:00
class DeskProTicketAdmin(admin.ModelAdmin):
list_display = ("id", "subject", "user", "created", "published")
2019-12-05 16:57:52 +00:00
readonly_fields = ("user",)
2018-11-08 19:45:21 +00:00
admin.site.register(Facility, FacilityAdmin)
admin.site.register(InternetExchange, InternetExchangeAdmin)
admin.site.register(InternetExchangeFacility, InternetExchangeFacilityAdmin)
2018-11-08 19:45:21 +00:00
admin.site.register(IXLan, IXLanAdmin)
admin.site.register(IXLanPrefix, IXLanPrefixAdmin)
admin.site.register(NetworkIXLan, NetworkIXLanAdmin)
admin.site.register(NetworkContact, NetworkContactAdmin)
admin.site.register(NetworkFacility, NetworkFacilityAdmin)
2018-11-08 19:45:21 +00:00
admin.site.register(Network, NetworkAdmin)
admin.site.register(User, UserAdmin)
admin.site.register(VerificationQueueItem, VerificationQueueAdmin)
admin.site.register(Sponsorship, SponsorshipAdmin)
admin.site.register(Partnership, PartnershipAdmin)
admin.site.register(OrganizationMerge, OrganizationMergeLog)
admin.site.register(UserPermission, UserPermissionAdmin)
admin.site.register(DuplicateIPNetworkIXLan, DuplicateIPAdmin)
admin.site.register(IXLanIXFMemberImportLog, IXLanIXFMemberImportLogAdmin)
admin.site.register(CommandLineTool, CommandLineToolAdmin)
admin.site.register(UserOrgAffiliationRequest, UserOrgAffiliationRequestAdmin)
admin.site.register(DeskProTicket, DeskProTicketAdmin)