Skip to content

Commit 7e941ae

Browse files
authored
[feature] Added WHOIS details to the UI #1026
Closes #1026 Signed-off-by: DragnEmperor <[email protected]>
1 parent 3ffae10 commit 7e941ae

File tree

16 files changed

+492
-44
lines changed

16 files changed

+492
-44
lines changed

docs/user/rest-api.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ List Devices
6868
6969
GET /api/v1/controller/device/
7070
71+
.. _device_list_whois:
72+
73+
**WHOIS Details**
74+
75+
If :doc:`WHOIS Lookup feature <whois>` is enabled, each device in the list
76+
response will also include a ``whois_info`` field with related brief WHOIS
77+
information.
78+
7179
**Available filters**
7280

7381
You can filter a list of devices based on their configuration status using
@@ -145,6 +153,13 @@ Get Device Detail
145153
146154
GET /api/v1/controller/device/{id}/
147155
156+
.. _device_detail_whois:
157+
158+
**WHOIS Details**
159+
160+
If :doc:`WHOIS Lookup feature <whois>` is enabled, the response will also
161+
include a ``whois_info`` field with related detailed WHOIS information.
162+
148163
Download Device Configuration
149164
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
150165

docs/user/settings.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -755,8 +755,8 @@ documentation regarding automatic retries for known errors.
755755

756756
Allows enabling the optional :doc:`WHOIS Lookup feature <whois>`.
757757

758-
.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.2/whois-admin-setting.png
759-
:target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.2/whois-admin-setting.png
758+
.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.3/whois-admin-setting.png
759+
:target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.3/whois-admin-setting.png
760760
:alt: WHOIS admin setting
761761

762762
After enabling this feature, you have to set

docs/user/whois.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,20 @@ Setup Instructions
7878
- Set :ref:`OPENWISP_CONTROLLER_WHOIS_ENABLED` to ``True``.
7979
- Set :ref:`OPENWISP_CONTROLLER_WHOIS_GEOIP_ACCOUNT` to **Account ID**.
8080
- Set :ref:`OPENWISP_CONTROLLER_WHOIS_GEOIP_KEY` to **License Key**.
81+
82+
Viewing WHOIS Lookup Data
83+
-------------------------
84+
85+
Once the WHOIS Lookup feature is enabled and WHOIS data is available, the
86+
retrieved details can be viewed in the following locations:
87+
88+
- **Device Admin**: On the device's admin page, the WHOIS data is
89+
displayed alongside the device's last IP address.
90+
91+
.. image:: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.3/whois-admin-details.png
92+
:target: https://raw.githubusercontent.com/openwisp/openwisp-controller/docs/docs/1.3/whois-admin-details.png
93+
:alt: WHOIS admin details
94+
95+
- **Device REST API**: See WHOIS details in the :ref:`Device List
96+
<device_list_whois>` and :ref:`Device Detail <device_detail_whois>`
97+
responses.

openwisp_controller/config/admin.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from swapper import load_model
3232

3333
from openwisp_controller.config.views import get_default_values, get_relevant_templates
34+
from openwisp_controller.config.whois.utils import get_whois_info
3435
from openwisp_users.admin import OrganizationAdmin
3536
from openwisp_users.multitenancy import MultitenantOrgFilter
3637
from openwisp_utils.admin import (
@@ -49,6 +50,7 @@
4950

5051
logger = logging.getLogger(__name__)
5152
prefix = "config/"
53+
whois_prefix = "whois/"
5254
Config = load_model("config", "Config")
5355
Device = load_model("config", "Device")
5456
DeviceGroup = load_model("config", "DeviceGroup")
@@ -559,12 +561,21 @@ class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
559561
fields.insert(0, "hardware_id")
560562
list_select_related = ("config", "organization")
561563

562-
class Media(BaseConfigAdmin.Media):
564+
# overriding media property to allow testing in isolation
565+
# as class Media is evaluated at import time making the
566+
# settings not overridable in tests
567+
@property
568+
def media(self):
563569
js = BaseConfigAdmin.Media.js + [
564570
f"{prefix}js/tabs.js",
565571
f"{prefix}js/management_ip.js",
566572
f"{prefix}js/relevant_templates.js",
567573
]
574+
css = BaseConfigAdmin.Media.css["all"]
575+
if app_settings.WHOIS_CONFIGURED:
576+
js.append(f"{whois_prefix}js/whois.js")
577+
css += (f"{whois_prefix}css/whois.css",)
578+
return super().media + forms.Media(js=js, css={"all": css})
568579

569580
def has_change_permission(self, request, obj=None):
570581
perm = super().has_change_permission(request)
@@ -918,6 +929,10 @@ def get_extra_context(self, pk=None):
918929
),
919930
}
920931
)
932+
# passing the whois details to the context to avoid
933+
# the need to make an additional request in the js
934+
if data := get_whois_info(pk):
935+
ctx["device_whois_details"] = json.dumps(data)
921936
return ctx
922937

923938
def add_view(self, request, form_url="", extra_context=None):

openwisp_controller/config/api/serializers.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from ...serializers import BaseSerializer
1111
from .. import settings as app_settings
12+
from ..whois.mixins import BriefWHOISMixin, WHOISMixin
1213

1314
Template = load_model("config", "Template")
1415
Vpn = load_model("config", "Vpn")
@@ -220,7 +221,7 @@ class DeviceListConfigSerializer(BaseConfigSerializer):
220221
templates = FilterTemplatesByOrganization(many=True, write_only=True)
221222

222223

223-
class DeviceListSerializer(DeviceConfigSerializer):
224+
class DeviceListSerializer(BriefWHOISMixin, DeviceConfigSerializer):
224225
config = DeviceListConfigSerializer(required=False)
225226

226227
class Meta(BaseMeta):
@@ -274,7 +275,7 @@ class DeviceDetailConfigSerializer(BaseConfigSerializer):
274275
templates = FilterTemplatesByOrganization(many=True)
275276

276277

277-
class DeviceDetailSerializer(DeviceConfigSerializer):
278+
class DeviceDetailSerializer(WHOISMixin, DeviceConfigSerializer):
278279
config = DeviceDetailConfigSerializer(allow_null=True)
279280
is_deactivated = serializers.BooleanField(read_only=True)
280281

openwisp_controller/config/migrations/0060_whoisinfo.py renamed to openwisp_controller/config/migrations/0061_whoisinfo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
class Migration(migrations.Migration):
1212
dependencies = [
13-
("config", "0059_zerotier_templates_ow_zt_to_global"),
13+
("config", "0060_cleanup_api_task_notification_types"),
1414
]
1515

1616
operations = [
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
:root {
2+
--whois-bg-light: #f9fafb;
3+
--whois-text-muted: rgba(0, 0, 0, 0.45);
4+
--whois-border-radius: 5px;
5+
--whois-padding: 15px;
6+
}
7+
8+
.whois-table,
9+
.whois {
10+
margin-top: 1.25em;
11+
max-width: 500px;
12+
width: 100%;
13+
border-radius: var(--whois-border-radius);
14+
}
15+
.whois-table {
16+
border-spacing: 0;
17+
box-shadow: 0 0 0 1px #ccc;
18+
border-style: none;
19+
}
20+
.whois-table th,
21+
.whois-table td {
22+
padding: var(--whois-padding) !important;
23+
border: none;
24+
}
25+
.whois-table tr:first-child {
26+
background-color: var(--whois-bg-light);
27+
}
28+
.whois-table tr:not(:first-child) {
29+
background-color: white;
30+
}
31+
.whois-table th:first-of-type > div {
32+
display: flex;
33+
align-items: center;
34+
}
35+
/* For rounding the individual corners */
36+
.whois-table th:first-of-type {
37+
border-top-left-radius: var(--whois-border-radius);
38+
border-right: 1px solid var(--border-color);
39+
border-bottom: 1px solid var(--border-color);
40+
}
41+
.whois-table th:last-of-type {
42+
border-top-right-radius: var(--whois-border-radius);
43+
border-bottom: 1px solid var(--border-color);
44+
}
45+
.whois-table tr:last-child td:first-child {
46+
border-bottom-left-radius: var(--whois-border-radius);
47+
border-right: 1px solid var(--border-color);
48+
}
49+
.whois-table tr:last-child td:last-child {
50+
border-bottom-right-radius: var(--whois-border-radius);
51+
}
52+
.whois-table .ow-info-icon {
53+
width: 1rem;
54+
height: 1rem;
55+
mask-repeat: no-repeat;
56+
mask-position: center;
57+
mask-size: contain;
58+
display: inline-block;
59+
flex-shrink: 0;
60+
background: #777;
61+
margin-left: 1em;
62+
}
63+
.whois-table .ow-info-icon:hover {
64+
background: #007cba;
65+
}
66+
67+
.whois {
68+
background: var(--whois-bg-light);
69+
}
70+
.whois summary {
71+
background: white;
72+
border-radius: var(--whois-border-radius);
73+
list-style: none;
74+
display: flex;
75+
align-items: center;
76+
color: var(--whois-text-muted) !important;
77+
font-weight: 600;
78+
cursor: pointer;
79+
}
80+
details.whois[open] summary {
81+
padding-bottom: 0 !important;
82+
}
83+
.whois summary > div {
84+
display: flex;
85+
width: 100%;
86+
justify-content: space-between;
87+
margin-left: 1.4em;
88+
}
89+
.whois summary::-webkit-details-marker {
90+
display: none;
91+
}
92+
.whois summary .mg-arrow {
93+
transform: scale(1.75);
94+
display: inline;
95+
}
96+
.whois > div {
97+
padding: var(--whois-padding);
98+
font-size: 1em;
99+
}
100+
.whois .additional-text {
101+
display: block;
102+
}
103+
.whois .additional-text + .additional-text {
104+
margin-top: 5px;
105+
}
106+
.whois-globe {
107+
flex-shrink: 0;
108+
mask: url(../images/whois_globe.svg) no-repeat center;
109+
-webkit-mask: url(../images/whois_globe.svg) no-repeat center;
110+
width: 1.1rem;
111+
height: 1.1rem;
112+
background-color: var(--whois-text-muted);
113+
}
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"use strict";
2+
3+
if (typeof gettext === "undefined") {
4+
var gettext = function (word) {
5+
return word;
6+
};
7+
}
8+
9+
django.jQuery(function ($) {
10+
const $addForm = $(".add-form");
11+
const $deviceForm = $("#device_form");
12+
13+
if (
14+
$addForm.length ||
15+
!$deviceForm.length ||
16+
typeof deviceWHOISDetails === "undefined"
17+
) {
18+
return;
19+
}
20+
const $parentDiv = $("#overview-group .field-last_ip div:last");
21+
const tooltipText = gettext(
22+
"This is the Organization associated with registered ASN",
23+
);
24+
25+
$parentDiv.after(
26+
`<table class="whois-table">
27+
<tr>
28+
<th>
29+
<div>
30+
<span>${gettext("ISP")}</span>
31+
<span title="${tooltipText}" class="ow-info-icon icon"></span>
32+
</div>
33+
</th>
34+
<th>${gettext("Country")}</th>
35+
</tr>
36+
<tr>
37+
<td>${deviceWHOISDetails.isp}</td>
38+
<td>${deviceWHOISDetails.address.country}</td>
39+
</tr>
40+
</table>
41+
<details class="whois">
42+
<summary>
43+
<span class="whois-globe"></span>
44+
<div>
45+
<span>${gettext("Additional Details")}</span><span class="mg-arrow"></span>
46+
</div>
47+
</summary>
48+
<div>
49+
<span class="additional-text">${gettext("ASN")}: ${deviceWHOISDetails.asn}</span>
50+
<span class="additional-text">${gettext("Timezone")}: ${deviceWHOISDetails.timezone}</span>
51+
<span class="additional-text">${gettext("Address")}: ${deviceWHOISDetails.formatted_address}</span>
52+
<span class="additional-text">${gettext("CIDR")}: ${deviceWHOISDetails.cidr}</span>
53+
</div>
54+
</details>`,
55+
);
56+
});

openwisp_controller/config/templates/admin/config/change_form.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
const owControllerApiHost = window.location;
2020
{% endif %}
2121
const owCommandApiEndpoint = '{{ commands_api_endpoint | safe }}';
22+
{% if device_whois_details %}
23+
const deviceWHOISDetails = {{ device_whois_details | safe }};
24+
{% endif %}
2225
</script>
2326
{{ block.super}}
2427
{% endblock extrahead %}

0 commit comments

Comments
 (0)