436 lines
12 KiB
Python
436 lines
12 KiB
Python
# Copyright 2020 Istvan Ruzman
|
|
# SPDX-License-Identifier: MIT OR Apache-2.0
|
|
|
|
from io import StringIO
|
|
|
|
import pytest
|
|
|
|
from pyrad3.dictionary import Dictionary, ParseError
|
|
from pyrad3.types import Encrypt
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"filename", ["dictionaries/self_recursive", "dictionaries/mutual_recursive"]
|
|
)
|
|
def test_dictionary_recursion(filename):
|
|
with pytest.raises(ParseError):
|
|
Dictionary("tests/" + filename)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"line",
|
|
[
|
|
"$INCLUDE",
|
|
"BEGIN-VENDOR",
|
|
"END-VENDOR",
|
|
"VENDOR",
|
|
"VENDOR NAME",
|
|
"ATTRIBUTE",
|
|
"ATTRIBUTE NAME",
|
|
"VALUE",
|
|
"VALUE ATTRNAME",
|
|
"VALUE ATTRNAME VALUENAME",
|
|
],
|
|
)
|
|
def test_lines_missing_tokens(line):
|
|
dictionary = StringIO(line)
|
|
with pytest.raises(ParseError):
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
def test_invalid_token():
|
|
dictionary = StringIO("invalid_token")
|
|
with pytest.raises(ParseError):
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"vendor",
|
|
[
|
|
"VENDOR test 1234",
|
|
"VENDOR test 1234 format=1,1",
|
|
"VENDOR test 1234 format=2,2",
|
|
"VENDOR test 1234 format=1,2",
|
|
"VENDOR test 1234 format=4,2",
|
|
"VENDOR test 1234 format=4,0",
|
|
"VENDOR WiMAX 1234 format=1,1,c",
|
|
],
|
|
)
|
|
def test_valid_vendor_definitions(vendor):
|
|
dictionary = StringIO(vendor)
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
def test_closing_wrong_vendor():
|
|
dictionary = StringIO(
|
|
"VENDOR TEST-VENDOR 1234\n" "BEGIN-VENDOR TEST-VENDOR\n" "END-VENDOR WRONG-VENDOR"
|
|
)
|
|
with pytest.raises(ParseError):
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
def test_nested_vendor():
|
|
dictionary = StringIO(
|
|
"VENDOR TEST-VENDOR1 1234\n"
|
|
"VENDOR TEST-VENDOR2 1235\n"
|
|
"BEGIN-VENDOR TEST-VENDOR1\n"
|
|
"BEGIN-VENDOR TEST-VENDOR2"
|
|
)
|
|
with pytest.raises(ParseError):
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
def test_begin_vendor_without_definition():
|
|
dictionary = StringIO("BEGIN-VENDOR TEST-VENDOR")
|
|
with pytest.raises(ParseError):
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"vendor",
|
|
[
|
|
"VENDOR test 1234 1,1",
|
|
"VENDOR test 1234 format=3,1",
|
|
"VENDOR test 1234 format=2",
|
|
"VENDOR test 1234 format=1,2,c",
|
|
"VENDOR test 1234 format=1,9",
|
|
"VENDOR test 1234 format=4,4 suffix",
|
|
"VENDOR test 1234 format=a,b suffix",
|
|
],
|
|
)
|
|
def test_invalid_vendor_definitions(vendor):
|
|
dictionary = StringIO(vendor)
|
|
with pytest.raises(ParseError):
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"number",
|
|
[
|
|
"ATTRIBUTE NAME 0x01 byte",
|
|
"ATTRIBUTE NAME 0x0001 byte",
|
|
"ATTRIBUTE NAME 0o123 byte",
|
|
"ATTRIBUTE NAME 5 byte",
|
|
],
|
|
)
|
|
def test_valid_attribute_numbers(number):
|
|
dictionary = StringIO(number)
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"invalid_number",
|
|
["1000", "ABCD", "-1", "inf", "INF", "-INF", "2e4", "2.5e3"],
|
|
)
|
|
def test_invalid_attribute_numbers(invalid_number):
|
|
dictionary = StringIO(f"ATTRIBUTE NAME {invalid_number} integer64")
|
|
with pytest.raises(ParseError):
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
@pytest.mark.parametrize("type_length", [1, 2, 4])
|
|
def test_attribute_number_limits(type_length):
|
|
too_big = 2 ** (8 * type_length)
|
|
max_value = too_big - 1
|
|
dictionary = StringIO(
|
|
f"VENDOR TEST 1234 format={type_length},1\n"
|
|
"BEGIN-VENDOR TEST\n"
|
|
f"ATTRIBUTE TEST {max_value} byte\n"
|
|
"END-VENDOR TEST\n"
|
|
)
|
|
Dictionary("", dictionary)
|
|
dictionary = StringIO(
|
|
f"VENDOR TEST 1234 format={type_length},1\n"
|
|
"BEGIN-VENDOR TEST\n"
|
|
f"ATTRIBUTE TEST {too_big} byte\n"
|
|
"END-VENDOR TEST\n"
|
|
)
|
|
with pytest.raises(ParseError):
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
def test_invalid_attr_type():
|
|
dictionary = StringIO("ATTRIBUTE NAME 2 invalid")
|
|
with pytest.raises(ParseError):
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
@pytest.mark.parametrize("value", ["1", "0x1", "0o1"])
|
|
def test_value_definition(value):
|
|
dictionary = StringIO(
|
|
"ATTRIBUTE TEST-ATTRIBUTE 1 byte\n" f"VALUE TEST-ATTRIBUTE TEST-VALUE {value}"
|
|
)
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"value_num, attr_type",
|
|
[
|
|
(0, "byte"),
|
|
(255, "byte"),
|
|
(0, "short"),
|
|
(2 ** 16 - 1, "short"),
|
|
(0, "integer"),
|
|
(2 ** 32 - 1, "integer"),
|
|
((-(2 ** 31)), "signed"),
|
|
(2 ** 31 - 1, "signed"),
|
|
(0, "integer64"),
|
|
(2 ** 64 - 1, "integer64"),
|
|
],
|
|
)
|
|
def test_value_number_within_limit(value_num, attr_type):
|
|
dictionary = StringIO(
|
|
f"ATTRIBUTE TEST-ATTRIBUTE 1 {attr_type}\n"
|
|
f"VALUE TEST-ATTRIBUTE TEST-VALUE {value_num}"
|
|
)
|
|
Dictionary("", dictionary)
|
|
dictionary = StringIO(
|
|
"VENDOR TEST-VENDOR 1234\n"
|
|
"BEGIN-VENDOR TEST-VENDOR\n"
|
|
f"ATTRIBUTE TEST-ATTRIBUTE 1 {attr_type}\n"
|
|
f"VALUE TEST-ATTRIBUTE TEST-VALUE {value_num}\n"
|
|
"END-VENDOR TEST-VEDNOR"
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"value_num, attr_type",
|
|
[
|
|
(-1, "byte"),
|
|
(256, "byte"),
|
|
(-1, "short"),
|
|
(2 ** 16, "short"),
|
|
(-1, "integer"),
|
|
(2 ** 32, "integer"),
|
|
(2 ** 31, "signed"),
|
|
((-(2 ** 31)) - 1, "signed"),
|
|
(-1, "integer64"),
|
|
(2 ** 64, "integer64"),
|
|
],
|
|
)
|
|
def test_value_number_out_of_limit(value_num, attr_type):
|
|
dictionary = StringIO(
|
|
f"ATTRIBUTE TEST-ATTRIBUTE 1 {attr_type}\n"
|
|
f"VALUE TEST-ATTRIBUTE TEST-VALUE {value_num}"
|
|
)
|
|
with pytest.raises(ParseError):
|
|
Dictionary("", dictionary)
|
|
dictionary = StringIO(
|
|
"VENDOR TEST-VENDOR 1234\n"
|
|
"BEGIN-VENDOR TEST-VENDOR\n"
|
|
f"ATTRIBUTE TEST-ATTRIBUTE 1 {attr_type}\n"
|
|
f"VALUE TEST-ATTRIBUTE TEST-VALUE {value_num}\n"
|
|
"END-VENDOR TEST-VEDNOR"
|
|
)
|
|
with pytest.raises(ParseError):
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"datatype",
|
|
[
|
|
"string",
|
|
"octets",
|
|
"abinary",
|
|
"byte",
|
|
"short",
|
|
"integer",
|
|
"signed",
|
|
"integer64",
|
|
"ipaddr",
|
|
"ipv4prefix",
|
|
"ipv6addr",
|
|
"ipv6prefix",
|
|
"combo-ip",
|
|
"ifid",
|
|
"ether",
|
|
"concat",
|
|
"tlv",
|
|
"extended",
|
|
"long-extended",
|
|
],
|
|
)
|
|
def test_all_datatypes_rfc_space(datatype):
|
|
dictionary = StringIO(f"ATTRIBUTE TEST-ATTRIBUTE 1 {datatype}\n")
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
def test_evs_datatype():
|
|
dictionary = StringIO("ATTRIBUTE TEST-ATTRIBUTE 1.10 evs\n")
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"datatype",
|
|
[
|
|
"string",
|
|
"octets",
|
|
"abinary",
|
|
"byte",
|
|
"short",
|
|
"integer",
|
|
"signed",
|
|
"integer64",
|
|
"ipaddr",
|
|
"ipv4prefix",
|
|
"ipv6addr",
|
|
"ipv6prefix",
|
|
"combo-ip",
|
|
"ifid",
|
|
"ether",
|
|
"tlv",
|
|
],
|
|
)
|
|
def test_valid_datatypes_in_vendor_space(datatype):
|
|
dictionary = StringIO(
|
|
"VENDOR TEST 1234\n"
|
|
"BEGIN-VENDOR TEST\n"
|
|
f"ATTRIBUTE TEST-ATTRIBUTE 1 {datatype}\n"
|
|
"END-VENDOR TEST\n"
|
|
)
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
@pytest.mark.parametrize("datatype", ["concat", "extended", "long-extended", "evs"])
|
|
def test_invalid_datatypes_in_vendor_space(datatype):
|
|
dictionary = StringIO(
|
|
"VENDOR TEST 1234\n"
|
|
"BEGIN-VENDOR TEST\n"
|
|
f"ATTRIBUTE TEST-ATTRIBUTE 1 {datatype}\n"
|
|
"END-VENDOR TEST\n"
|
|
)
|
|
with pytest.raises(ParseError):
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"invalid_number",
|
|
["ABCD", "-1", "inf", "INF", "-INF", "0.1", "2e4", "2.5e3"],
|
|
)
|
|
def test_invalid_value_numbers(invalid_number):
|
|
dictionary = StringIO(
|
|
f"ATTRIBUTE TEST-ATTRIBUTE 1 integer\n"
|
|
f"VALUE TEST-ATTRIBUTE TEST-VALUE {invalid_number}"
|
|
)
|
|
with pytest.raises(ParseError):
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
def test_value_for_non_existing_attribute():
|
|
dictionary = StringIO("VALUE ATTRNAME VALUENAME 1234")
|
|
with pytest.raises(ParseError):
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"datatype",
|
|
[
|
|
"string",
|
|
"octets",
|
|
"abinary",
|
|
"ipaddr",
|
|
"ipv4prefix",
|
|
"ipv6addr",
|
|
"ipv6prefix",
|
|
"combo-ip",
|
|
"ifid",
|
|
"ether",
|
|
"tlv",
|
|
],
|
|
)
|
|
def test_value_for_wrong_datatype(datatype):
|
|
dictionary = StringIO(f"ATTRIBUTE NAME 123 {datatype}\n" "VALUE NAME VNAME 256")
|
|
with pytest.raises(ParseError):
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
def test_unimplemented_tlvs():
|
|
dictionary = StringIO("BEGIN-TLV")
|
|
with pytest.raises(NotImplementedError):
|
|
Dictionary("", dictionary)
|
|
dictionary = StringIO("END-TLV")
|
|
with pytest.raises(NotImplementedError):
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
@pytest.mark.parametrize("flag", [1, 2, 3])
|
|
def test_valid_attribute_encrpytion_flags(flag):
|
|
dictionary = StringIO(f"ATTRIBUTE NAME 123 octets encrypt={flag}")
|
|
rad_dict = Dictionary("", dictionary)
|
|
assert rad_dict.attrindex[123].encrypt == Encrypt(flag)
|
|
|
|
|
|
@pytest.mark.parametrize("flag", ["0.1", "0", "4", "0x1", "0o2", "user", ""])
|
|
def test_invalid_attribute_encrpytion_flags(flag):
|
|
dictionary = StringIO(f"ATTRIBUTE NAME 123 octets encrypt={flag}")
|
|
with pytest.raises(ParseError):
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
def test_has_tag_flag():
|
|
dictionary = StringIO("ATTRIBUTE NAME 123 octets has_tag")
|
|
rad_dict = Dictionary("", dictionary)
|
|
assert rad_dict.attrindex[123].has_tag
|
|
|
|
|
|
@pytest.mark.parametrize("invalid_flag", ["blablub", "encrypt=2=2", "concat"])
|
|
def test_invalid_attribute_flags(invalid_flag):
|
|
dictionary = StringIO(f"ATTRIBUTE NAME 123 octets {invalid_flag}")
|
|
with pytest.raises(ParseError):
|
|
Dictionary("", dictionary)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"attribute", ["RFC-ATTRIBUTE", "VENDOR-ATTRIBUTE", 7, (26, 5555, 7)]
|
|
)
|
|
def test_get_attributes_from_dictionaries(attribute):
|
|
dictionary = StringIO(
|
|
"ATTRIBUTE RFC-ATTRIBUTE 7 integer\n"
|
|
"VENDOR TEST-VENDOR 5555\n"
|
|
"BEGIN-VENDOR TEST-VENDOR\n"
|
|
"ATTRIBUTE VENDOR-ATTRIBUTE 7 integer\n"
|
|
"END-VENDOR TEST-VENDOR"
|
|
)
|
|
dd = Dictionary("", dictionary)
|
|
_ = dd[attribute]
|
|
|
|
|
|
@pytest.mark.parametrize("attribute", ["RFC-ATTRIBUTE1", 8, (26, 5556, 7), (26, 5555, 8)])
|
|
def test_get_nonexisting_attributes_from_dictionaries(attribute):
|
|
dictionary = StringIO(
|
|
"ATTRIBUTE RFC-ATTRIBUTE 7 integer\n"
|
|
"VENDOR TEST-VENDOR 5555\n"
|
|
"BEGIN-VENDOR TEST-VENDOR\n"
|
|
"ATTRIBUTE VENDOR-ATTRIBUTE 7 integer\n"
|
|
"END-VENDOR TEST-VENDOR"
|
|
)
|
|
dd = Dictionary("", dictionary)
|
|
with pytest.raises(KeyError):
|
|
_ = dd[attribute]
|
|
|
|
|
|
def test_extended_evs():
|
|
dictionary = StringIO(
|
|
"ATTRIBUTE RFC-EXTENDED 10 extended\n"
|
|
"ATTRIBUTE RFC-EXTENDED-EVS 10.20 evs\n"
|
|
"VENDOR TEST-VENDOR 1234\n"
|
|
"BEGIN-VENDOR TEST-VENDOR format=RFC-EXTENDED-EVS\n"
|
|
"ATTRIBUTE VENDOR-ATTRIBUTE 10 integer\n"
|
|
"END-VENDOR TEST-VENDOR"
|
|
)
|
|
dd = Dictionary("", dictionary)
|
|
assert dd["VENDOR-ATTRIBUTE"].code == [10, 20, 1234, 10]
|
|
|
|
|
|
def test_long_extended_evs():
|
|
dictionary = StringIO(
|
|
"ATTRIBUTE RFC-LONGEXTENDED 10 extended\n"
|
|
"ATTRIBUTE RFC-LONGEXTENDED-EVS 10.20 evs\n"
|
|
"VENDOR TEST-VENDOR 1234\n"
|
|
"BEGIN-VENDOR TEST-VENDOR format=RFC-LONGEXTENDED-EVS\n"
|
|
"ATTRIBUTE VENDOR-ATTRIBUTE 10 integer\n"
|
|
"END-VENDOR TEST-VENDOR"
|
|
)
|
|
dd = Dictionary("", dictionary)
|
|
assert dd["VENDOR-ATTRIBUTE"].code == [10, 20, 1234, 10]
|