···
2
-
ldapDomain = "example.org";
3
-
ldapSuffix = "dc=example,dc=org";
5
-
ldapRootUser = "admin";
6
-
ldapRootPassword = "foobar";
9
-
testPassword = "verySecure";
10
-
testGroup = "netbox-users";
12
-
import ../make-test-python.nix (
22
-
meta = with lib.maintainers; {
31
-
virtualisation.memorySize = 2048;
35
-
secretKeyFile = pkgs.writeText "secret" ''
36
-
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
40
-
ldapConfigPath = pkgs.writeText "ldap_config.py" ''
42
-
from django_auth_ldap.config import LDAPSearch, PosixGroupType
44
-
AUTH_LDAP_SERVER_URI = "ldap://localhost/"
46
-
AUTH_LDAP_USER_SEARCH = LDAPSearch(
47
-
"ou=accounts,ou=posix,${ldapSuffix}",
52
-
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
53
-
"ou=groups,ou=posix,${ldapSuffix}",
55
-
"(objectClass=posixGroup)",
57
-
AUTH_LDAP_GROUP_TYPE = PosixGroupType()
59
-
# Mirror LDAP group assignments.
60
-
AUTH_LDAP_MIRROR_GROUPS = True
62
-
# For more granular permissions, we can map LDAP groups to Django groups.
63
-
AUTH_LDAP_FIND_GROUP_PERMS = True
70
-
recommendedProxySettings = true;
72
-
virtualHosts.netbox = {
74
-
locations."/".proxyPass = "http://localhost:${toString config.services.netbox.port}";
75
-
locations."/static/".alias = "/var/lib/netbox/static/";
79
-
# Adapted from the sssd-ldap NixOS test
80
-
services.openldap = {
84
-
"cn=schema".includes = [
85
-
"${pkgs.openldap}/etc/schema/core.ldif"
86
-
"${pkgs.openldap}/etc/schema/cosine.ldif"
87
-
"${pkgs.openldap}/etc/schema/inetorgperson.ldif"
88
-
"${pkgs.openldap}/etc/schema/nis.ldif"
90
-
"olcDatabase={1}mdb" = {
96
-
olcDatabase = "{1}mdb";
97
-
olcDbDirectory = "/var/lib/openldap/db";
98
-
olcSuffix = ldapSuffix;
99
-
olcRootDN = "cn=${ldapRootUser},${ldapSuffix}";
100
-
olcRootPW = ldapRootPassword;
105
-
declarativeContents = {
109
-
objectClass: dcObject
110
-
objectClass: organization
113
-
dn: ou=posix,${ldapSuffix}
115
-
objectClass: organizationalUnit
117
-
dn: ou=accounts,ou=posix,${ldapSuffix}
119
-
objectClass: organizationalUnit
121
-
dn: uid=${testUser},ou=accounts,ou=posix,${ldapSuffix}
122
-
objectClass: person
123
-
objectClass: posixAccount
124
-
userPassword: ${testPassword}
125
-
homeDirectory: /home/${testUser}
131
-
dn: ou=groups,ou=posix,${ldapSuffix}
133
-
objectClass: organizationalUnit
135
-
dn: cn=${testGroup},ou=groups,ou=posix,${ldapSuffix}
136
-
objectClass: posixGroup
138
-
memberUid: ${testUser}
143
-
users.users.nginx.extraGroups = [ "netbox" ];
145
-
networking.firewall.allowedTCPPorts = [ 80 ];
150
-
changePassword = pkgs.writeText "change-password.py" ''
151
-
from users.models import User
152
-
u = User.objects.get(username='netbox')
153
-
u.set_password('netbox')
158
-
from typing import Any, Dict
162
-
machine.wait_for_unit("netbox.target")
163
-
machine.wait_until_succeeds("journalctl --since -1m --unit netbox --grep Listening")
165
-
with subtest("Home screen loads"):
167
-
"curl -sSfL http://[::1]:8001 | grep '<title>Home | NetBox</title>'"
170
-
with subtest("Staticfiles are generated"):
171
-
machine.succeed("test -e /var/lib/netbox/static/netbox.js")
173
-
with subtest("Superuser can be created"):
175
-
"netbox-manage createsuperuser --noinput --username netbox --email netbox@example.com"
177
-
# Django doesn't have a "clean" way of inputting the password from the command line
178
-
machine.succeed("cat '${changePassword}' | netbox-manage shell")
180
-
machine.wait_for_unit("network.target")
182
-
with subtest("Home screen loads from nginx"):
184
-
"curl -sSfL http://localhost | grep '<title>Home | NetBox</title>'"
187
-
with subtest("Staticfiles can be fetched"):
188
-
machine.succeed("curl -sSfL http://localhost/static/netbox.js")
189
-
machine.succeed("curl -sSfL http://localhost/static/docs/")
191
-
def login(username: str, password: str):
192
-
encoded_data = json.dumps({"username": username, "password": password})
193
-
uri = "/users/tokens/provision/"
194
-
result = json.loads(
198
-
"-H 'Accept: application/json' "
199
-
"-H 'Content-Type: application/json' "
200
-
f"'http://localhost/api{uri}' "
201
-
f"--data '{encoded_data}'"
204
-
return result["key"]
206
-
with subtest("Can login"):
207
-
auth_token = login("netbox", "netbox")
213
-
"-H 'Accept: application/json' "
214
-
f"-H 'Authorization: Token {auth_token}' "
215
-
f"'http://localhost/api{uri}'"
219
-
def delete(uri: str):
220
-
return machine.succeed(
223
-
"-H 'Accept: application/json' "
224
-
f"-H 'Authorization: Token {auth_token}' "
225
-
f"'http://localhost/api{uri}'"
229
-
def data_request(uri: str, method: str, data: Dict[str, Any]):
230
-
encoded_data = json.dumps(data)
235
-
"-H 'Accept: application/json' "
236
-
"-H 'Content-Type: application/json' "
237
-
f"-H 'Authorization: Token {auth_token}' "
238
-
f"'http://localhost/api{uri}' "
239
-
f"--data '{encoded_data}'"
243
-
def post(uri: str, data: Dict[str, Any]):
244
-
return data_request(uri, "POST", data)
246
-
def patch(uri: str, data: Dict[str, Any]):
247
-
return data_request(uri, "PATCH", data)
249
-
with subtest("Can create objects"):
250
-
result = post("/dcim/sites/", {"name": "Test site", "slug": "test-site"})
251
-
site_id = result["id"]
254
-
# http://netbox.extra.cea.fr/static/docs/integrations/rest-api/#creating-a-new-object
255
-
post("/ipam/prefixes/", {"prefix": "192.0.2.0/24", "site": site_id})
258
-
"/dcim/manufacturers/",
259
-
{"name": "Test manufacturer", "slug": "test-manufacturer"}
261
-
manufacturer_id = result["id"]
263
-
# Had an issue with device-types before NetBox 3.4.0
265
-
"/dcim/device-types/",
267
-
"model": "Test device type",
268
-
"manufacturer": manufacturer_id,
269
-
"slug": "test-device-type",
272
-
device_type_id = result["id"]
274
-
with subtest("Can list objects"):
275
-
result = get("/dcim/sites/")
277
-
assert result["count"] == 1
278
-
assert result["results"][0]["id"] == site_id
279
-
assert result["results"][0]["name"] == "Test site"
280
-
assert result["results"][0]["description"] == ""
282
-
result = get("/dcim/device-types/")
283
-
assert result["count"] == 1
284
-
assert result["results"][0]["id"] == device_type_id
285
-
assert result["results"][0]["model"] == "Test device type"
287
-
with subtest("Can update objects"):
288
-
new_description = "Test site description"
289
-
patch(f"/dcim/sites/{site_id}/", {"description": new_description})
290
-
result = get(f"/dcim/sites/{site_id}/")
291
-
assert result["description"] == new_description
293
-
with subtest("Can delete objects"):
294
-
# Delete a device-type since no object depends on it
295
-
delete(f"/dcim/device-types/{device_type_id}/")
297
-
result = get("/dcim/device-types/")
298
-
assert result["count"] == 0
300
-
with subtest("Can use the GraphQL API"):
301
-
encoded_data = json.dumps({
302
-
"query": "query { prefix_list { prefix, site { id, description } } }",
304
-
result = json.loads(
307
-
"-H 'Accept: application/json' "
308
-
"-H 'Content-Type: application/json' "
309
-
f"-H 'Authorization: Token {auth_token}' "
310
-
"'http://localhost/graphql/' "
311
-
f"--data '{encoded_data}'"
315
-
assert len(result["data"]["prefix_list"]) == 1
316
-
assert result["data"]["prefix_list"][0]["prefix"] == "192.0.2.0/24"
317
-
assert result["data"]["prefix_list"][0]["site"]["id"] == str(site_id)
318
-
assert result["data"]["prefix_list"][0]["site"]["description"] == new_description
320
-
with subtest("Can login with LDAP"):
321
-
machine.wait_for_unit("openldap.service")
322
-
login("alice", "${testPassword}")
324
-
with subtest("Can associate LDAP groups"):
325
-
result = get("/users/users/?username=${testUser}")
327
-
assert result["count"] == 1
328
-
assert any(group["name"] == "${testGroup}" for group in result["results"][0]["groups"])