From 9deab00526050a8b174046470ff1cea18245dbc9 Mon Sep 17 00:00:00 2001 From: Eliott-B Date: Thu, 19 Dec 2024 14:35:38 +0100 Subject: [PATCH 1/2] Add doc for IPv4 route --- api/README.md | 62 +++++++++++++++++++++++++++++++++ api/src/routes/ipv4.py | 79 +++++++++++++++++++++++------------------- 2 files changed, 106 insertions(+), 35 deletions(-) diff --git a/api/README.md b/api/README.md index 8022364..5502ed4 100644 --- a/api/README.md +++ b/api/README.md @@ -7,6 +7,7 @@ - [Login](#login) - [Register](#register) - [Refresh token](#refresh-token) + - [Users](#users) - [Get all users](#get-all-users) - [Get user by username](#get-user-by-username) - [Get own user](#get-own-user) @@ -14,6 +15,15 @@ - [Update own user](#update-own-user) - [Update user password](#update-user-password) - [Delete user](#delete-user) + - [IPv4](#ipv4) + - [Get IPv4 class](#get-ipv4-class) + - [Convert dec to bin](#convert-dec-to-bin) + - [Convert dec to hex](#convert-dec-to-hex) + - [Convert hex to dec](#convert-hex-to-dec) + - [Convert bin to dec](#convert-bin-to-dec) + - [Get CIDR notation](#get-cidr-notation) + - [Get mask](#get-mask) + - [Get VLSM](#get-vlsm) - [IPv6](#ipv6) - [Simplify IPv6](#simplify-ipv6) - [Extend IPv6](#extend-ipv6) @@ -71,6 +81,8 @@ } ``` +### Users + #### Get all users | Method | URL | Description | Need token | Roles | @@ -139,6 +151,56 @@ | ------ | ----------------- | ----------- | ---------- | ----- | | DELETE | /users/{username} | Delete user | True | Admin | +### IPv4 + +#### Get IPv4 class + +| Method | URL | Description | Need token | Roles | +| ------ | ---------------- | -------------- | ---------- | ----- | +| GET | /ipv4/class/{ip} | Get IPv4 class | True | User | + +#### Convert dec to bin + +| Method | URL | Description | Need token | Roles | +| ------ | ---------------------- | ------------------ | ---------- | ----- | +| GET | /ipv4/dec-to-bin/{dec} | Convert dec to bin | True | User | + +#### Convert dec to hex + +| Method | URL | Description | Need token | Roles | +| ------ | ---------------------- | ------------------ | ---------- | ----- | +| GET | /ipv4/dec-to-hex/{dec} | Convert dec to hex | True | User | + +#### Convert hex to dec + +| Method | URL | Description | Need token | Roles | +| ------ | ---------------------- | ------------------ | ---------- | ----- | +| GET | /ipv4/hex-to-dec/{hex} | Convert hex to dec | True | User | + +#### Convert bin to dec + +| Method | URL | Description | Need token | Roles | +| ------ | ---------------------- | ------------------ | ---------- | ----- | +| GET | /ipv4/bin-to-dec/{bin} | Convert bin to dec | True | User | + +#### Get CIDR notation + +| Method | URL | Description | Need token | Roles | +| ------ | --------------- | ----------------- | ---------- | ----- | +| GET | /ipv4/cidr/{ip} | Get CIDR notation | True | User | + +#### Get mask + +| Method | URL | Description | Need token | Roles | +| ------ | --------------- | ----------- | ---------- | ----- | +| GET | /ipv4/mask/{ip} | Get mask | True | User | + +#### Get VLSM + +| Method | URL | Description | Need token | Roles | +| ------ | --------------- | ----------- | ---------- | ----- | +| GET | /ipv4/vlsm/{ip} | Get VLSM | True | User | + ### IPv6 #### Simplify IPv6 diff --git a/api/src/routes/ipv4.py b/api/src/routes/ipv4.py index 5471895..65cf943 100644 --- a/api/src/routes/ipv4.py +++ b/api/src/routes/ipv4.py @@ -2,70 +2,79 @@ from fastapi import APIRouter, Depends -import utils.IPV4 as ipv4_utils from dependencies.jwt import jwt_bearer +from utils.IPV4 import ( + bin_to_dec, + dec_to_bin, + dec_to_hex, + get_ipv4_mask, + hex_to_dec, + ipv4_class, + ipv4_to_cidr, + vlsm, +) router = APIRouter() -@router.get("/ipv4_class/{ipv4}", summary="Determines the class of an IPv4 address.",\ +@router.get("/class/{ipv4}", summary="Determine the class of an IPv4 address.",\ dependencies=[Depends(jwt_bearer)]) async def get_ipv4_class(ipv4: str) -> dict: - """Determines the class of an IPv4 address.""" - res = ipv4_utils.ipv4_class(ipv4) + """Determine the class of an IPv4 address.""" + res = ipv4_class(ipv4) return {"ipv4": res} -@router.get("/dec_to_bin/{ipv4}", summary="Converts an IPv4 address from decimal notation to binary notation",\ +@router.get("/dec_to_bin/{ipv4}",\ + summary="Convert an IPv4 address from decimal notation to binary notation",\ dependencies=[Depends(jwt_bearer)]) async def get_dec_to_bin(ipv4: str) -> dict: - """Converts an IPv4 address from decimal notation to binary notation""" - res = ipv4_utils.dec_to_bin(ipv4) + """Convert an IPv4 address from decimal notation to binary notation.""" + res = dec_to_bin(ipv4) return {"ipv4": res} -@router.get("/dec_to_hex/{ipv4}", summary="Converts an IPv4 address from decimal notation to hexadecimal notation",\ +@router.get("/dec_to_hex/{ipv4}",\ + summary="Convert an IPv4 address from decimal notation to hexadecimal notation",\ dependencies=[Depends(jwt_bearer)]) async def get_dec_to_hex(ipv4: str) -> dict: - """Converts an IPv4 address from decimal notation to hexadecimal notation""" - res = ipv4_utils.dec_to_hex(ipv4) + """Convert an IPv4 address from decimal notation to hexadecimal notation.""" + res = dec_to_hex(ipv4) return {"ipv4": res} -@router.get("/hex_to_dec/{ipv4}", summary="Converts an IPv4 address from hexadecimal notation to decimal notation.",\ +@router.get("/hex_to_dec/{ipv4}",\ + summary="Convert an IPv4 address from hexadecimal notation to decimal notation.",\ dependencies=[Depends(jwt_bearer)]) async def get_hex_to_dec(ipv4: str) -> dict: - """Converts an IPv4 address from hexadecimal notation to decimal notation.""" - res = ipv4_utils.hex_to_dec(ipv4) + """Convert an IPv4 address from hexadecimal notation to decimal notation.""" + res = hex_to_dec(ipv4) return {"ipv4": res} -@router.get("/bin_to_dec/{ipv4}", summary="Converts an IPv4 address from binary notation to decimal notation.",\ +@router.get("/bin_to_dec/{ipv4}",\ + summary="Convert an IPv4 address from binary notation to decimal notation.",\ dependencies=[Depends(jwt_bearer)]) async def get_bin_to_dec(ipv4: str) -> dict: - """Converts an IPv4 address from binary notation to decimal notation.""" - res = ipv4_utils.bin_to_dec(ipv4) + """Convert an IPv4 address from binary notation to decimal notation.""" + res = bin_to_dec(ipv4) return {"ipv4": res} -@router.get("/bin_to_dec/{ipv4}", summary="Converts an IPv4 address from binary notation to decimal notation.",\ +@router.get("/cidr/{ipv4}",\ + summary="Convert an IPv4 address to CIDR notation.",\ dependencies=[Depends(jwt_bearer)]) -async def get_bin_to_dec(ipv4: str) -> dict: - """Converts an IPv4 address from binary notation to decimal notation.""" - res = ipv4_utils.bin_to_dec(ipv4) +async def get_cidr(ipv4: str, mask: str) -> dict: + """Convert an IPv4 address to CIDR notation.""" + res = ipv4_to_cidr(ipv4,mask) return {"ipv4": res} -@router.get("/ipv4_to_cidr/{ipv4}", summary="Converts an IPv4 address to CIDR notation.",\ +@router.get("/mask/{ipv4}",\ + summary="Calculate the subnet mask from an IPv4 address in CIDR notation.",\ dependencies=[Depends(jwt_bearer)]) -async def get_ipv4_to_cidr(ipv4: str, mask: str) -> dict: - """Converts an IPv4 address to CIDR notation.""" - res = ipv4_utils.ipv4_to_cidr(ipv4,mask) +async def get_mask(ipv4: str) -> dict: + """Calculate the subnet mask from an IPv4 address in CIDR notation.""" + res = get_ipv4_mask(ipv4) return {"ipv4": res} -@router.get("/get_ivp4_mask/{ipv4}", summary="Calculates the subnet mask from an IPv4 address in CIDR notation.",\ - dependencies=[Depends(jwt_bearer)]) -async def get_ipv4_mask(ipv4: str) -> dict: - """Calculates the subnet mask from an IPv4 address in CIDR notation.""" - res = ipv4_utils.get_ipv4_mask(ipv4) - return {"ipv4": res} - -@router.get("/vlsm/{ipv4}", summary="Implements the VLSM (Variable Length Subnet Mask) technique.",\ +@router.get("/vlsm/{ipv4}",\ + summary="Implement the VLSM (Variable Length Subnet Mask) technique.",\ dependencies=[Depends(jwt_bearer)]) async def get_vlsm(baseip: str, subnet: str) -> dict: - """Implements the VLSM (Variable Length Subnet Mask) technique.""" - res = ipv4_utils.vlsm(baseip,subnet) - return {"ipv4": res} \ No newline at end of file + """Implement the VLSM (Variable Length Subnet Mask) technique.""" + res = vlsm(baseip,subnet) + return {"ipv4": res} From b6f6e57aa34885d618946d81c5bc281c6b81a2f8 Mon Sep 17 00:00:00 2001 From: Eliott-B Date: Thu, 19 Dec 2024 14:48:20 +0100 Subject: [PATCH 2/2] Fix linter errors and warnings --- api/src/utils/IPV4.py | 298 +++++++++++++++++++++++++----------------- 1 file changed, 177 insertions(+), 121 deletions(-) diff --git a/api/src/utils/IPV4.py b/api/src/utils/IPV4.py index 519f46b..efceb4b 100644 --- a/api/src/utils/IPV4.py +++ b/api/src/utils/IPV4.py @@ -1,174 +1,212 @@ -def is_valid(address): - """ - Checks if an IPv4 address is valid. - - :param address: A string representing the IPv4 address (e.g., "192.168.1.1"). - :return: True if the address is valid, False otherwise. +"""Module for IPv4 utilities.""" + +def is_valid(address: str) -> bool: + """Check if an IPv4 address is valid. + + Args: + address (str): A string representing the IPv4 address (e.g., "192.168.1.1"). + + Returns: + bool: True if the address is valid, False otherwise. + """ - octets = address.split('.') - - if len(octets) != 4: + octets = address.split(".") + + if len(octets) != 4: # noqa: PLR2004 return False - + for octet in octets: - if not octet.isdigit() or not (0 <= int(octet) <= 255): + if not octet.isdigit() or not (0 <= int(octet) <= 255): # noqa: PLR2004 return False - + return True -def ipv4_class(address): - """ - Determines the class A, B, or C of an IPv4 address. - - :param address: A string representing the IPv4 address (e.g., "192.168.1.1"). - :return: A string indicating the class of the address (A, B, or C) or an error message if the address is invalid. +def ipv4_class(address: str) -> str: + """Determine the class A, B, or C of an IPv4 address. + + Args: + address (str): A string representing the IPv4 address (e.g., "192.168.1.1"). + + Returns: + str: A string indicating the class of the address (A, B, or C) + or an error message if the address is invalid. + """ if not is_valid(address): return False - octets = address.split('.') + octets = address.split(".") first_octet = int(octets[0]) - - if 0 <= first_octet <= 127: + + if 0 <= first_octet <= 127: # noqa: PLR2004 return "Class A" - elif 128 <= first_octet <= 191: + if 128 <= first_octet <= 191: # noqa: PLR2004 return "Class B" - elif 192 <= first_octet <= 223: + if 192 <= first_octet <= 223: # noqa: PLR2004 return "Class C" - else: - return "The address does not belong to classes A, B, or C" - -def dec_to_bin(address): - """ - Converts an IPv4 address from decimal notation to binary notation. - - :param address: A string representing the IPv4 address (e.g., "192.168.1.1"). - :return: A string containing the IPv4 address in binary notation (e.g., "11000000.10101000.00000001.00000001") + + return "The address does not belong to classes A, B, or C" + +def dec_to_bin(address: str) -> str: + """Convert an IPv4 address from decimal notation to binary notation. + + Args: + address (str): A string representing the IPv4 address (e.g., "192.168.1.1"). + + Returns: + str: A string containing the IPv4 address in binary notation + (e.g., "11000000.10101000.00000001.00000001") or an error message if the address is invalid. + """ if not is_valid(address): return "Invalid IPv4 address" - - octets = address.split('.') - + + octets = address.split(".") + binary_octets = [f"{int(octet):08b}" for octet in octets] - - return '.'.join(binary_octets) -def dec_to_hex(address): - """ - Converts an IPv4 address from decimal notation to hexadecimal notation. - - :param address: A string representing the IPv4 address (e.g., "192.168.1.1"). - :return: A string containing the IPv4 address in hexadecimal notation (e.g., "C0.A8.01.01") + return ".".join(binary_octets) + +def dec_to_hex(address: str) -> str: + """Convert an IPv4 address from decimal notation to hexadecimal notation. + + Args: + address (str): A string representing the IPv4 address (e.g., "192.168.1.1"). + + Returns: + str: A string containing the IPv4 address in hexadecimal notation + (e.g., "C0.A8.01.01") or an error message if the address is invalid. + """ if not is_valid(address): return "Invalid IPv4 address" - - octets = address.split('.') - + + octets = address.split(".") + hex_octets = [f"{int(octet):02X}" for octet in octets] - - return '.'.join(hex_octets) -def hex_to_dec(address): - """ - Converts an IPv4 address from hexadecimal notation to decimal notation. - - :param address: A string representing the IPv4 address in hexadecimal notation (e.g., "C0.A8.01.01"). - :return: A string containing the IPv4 address in decimal notation (e.g., "192.168.1.1"), + return ".".join(hex_octets) + +def hex_to_dec(address: str) -> str: + """Convert an IPv4 address from hexadecimal notation to decimal notation. + + Args: + address (str): A string representing the IPv4 address in hexadecimal notation + (e.g., "C0.A8.01.01"). + + Returns: + str: A string containing the IPv4 address in decimal notation + (e.g., "192.168.1.1"), or an error message if the address is invalid. + """ - octets = address.split('.') - - if len(octets) != 4: + octets = address.split(".") + + if len(octets) != 4: # noqa: PLR2004 return "Invalid hexadecimal IPv4 address" - + try: decimal_octets = [str(int(octet, 16)) for octet in octets] except ValueError: return "Invalid hexadecimal IPv4 address" - - return '.'.join(decimal_octets) -def bin_to_dec(address): - """ - Converts an IPv4 address from binary notation to decimal notation. - - :param address: A string representing the IPv4 address in binary notation (e.g., "11000000.10101000.00000001.00000001"). - :return: A string containing the IPv4 address in decimal notation (e.g., "192.168.1.1"), + return ".".join(decimal_octets) + +def bin_to_dec(address: str) -> str: + """Convert an IPv4 address from binary notation to decimal notation. + + Args: + address (str): A string representing the IPv4 address in binary notation + (e.g., "11000000.10101000.00000001.00000001"). + + Returns: + str: A string containing the IPv4 address in decimal notation + (e.g., "192.168.1.1"), or an error message if the address is invalid. + """ - octets = address.split('.') - - if len(octets) != 4: + octets = address.split(".") + + if len(octets) != 4: # noqa: PLR2004 return "Invalid binary IPv4 address" - + try: decimal_octets = [str(int(octet, 2)) for octet in octets] except ValueError: return "Invalid binary IPv4 address" - - return '.'.join(decimal_octets) -def ipv4_to_cidr(address, mask): - """ - Converts an IPv4 address to CIDR notation. - - :param address: A string representing the IPv4 address (e.g., "192.168.1.1"). - :param mask: A string representing the subnet mask (e.g., "255.255.255.0"). - :return: A string representing the IPv4 address in CIDR notation (e.g., "192.168.1.1/24"), + return ".".join(decimal_octets) + +def ipv4_to_cidr(address: str, mask: str) -> str: + """Convert an IPv4 address to CIDR notation. + + Args: + address (str): A string representing the IPv4 address (e.g., "192.168.1.1"). + mask (str): A string representing the subnet mask (e.g., "255.255.255.0"). + + Returns: + str: A string representing the IPv4 address in CIDR notation + (e.g., "192.168.1.1/24"), or an error message if the address or mask is invalid. + """ if not is_valid(address): return "Invalid address" - - binary_mask = ''.join([f"{int(octet):08b}" for octet in mask.split('.')]) - ones_count = binary_mask.count('1') - + + binary_mask = "".join([f"{int(octet):08b}" for octet in mask.split(".")]) + ones_count = binary_mask.count("1") + return f"{address}/{ones_count}" -def get_ipv4_mask(cidr): - """ - Calculates the subnet mask from an IPv4 address in CIDR notation. - - :param cidr: A string representing an IPv4 address in CIDR notation (e.g., "192.168.1.1/24"). - :return: A string representing the subnet mask (e.g., "255.255.255.0"), +def get_ipv4_mask(cidr: str) -> str: + """Calculate the subnet mask from an IPv4 address in CIDR notation. + + Args: + cidr (str): A string representing an IPv4 address in CIDR notation + (e.g., "192.168.1.1/24"). + + Returns: + str: A string representing the subnet mask (e.g., "255.255.255.0"), or an error message if the input is invalid. + """ - if '/' not in cidr: + if "/" not in cidr: return "Invalid CIDR notation" - + try: - address, prefix = cidr.split('/') - prefix = int(prefix) + address, prefix = cidr.split("/") + prefix = int(prefix) except ValueError: return "Invalid CIDR notation" - - if not is_valid(address) or not (0 <= prefix <= 32): + + if not is_valid(address) or not (0 <= prefix <= 32): # noqa: PLR2004 return "Invalid IPv4 address or CIDR prefix" - - binary_mask = '1' * prefix + '0' * (32 - prefix) - + + binary_mask = "1" * prefix + "0" * (32 - prefix) + mask_octets = [int(binary_mask[i:i+8], 2) for i in range(0, 32, 8)] - - mask = '.'.join(map(str, mask_octets)) - - return mask -def vlsm(base_ip, subnets): - """ - Implements the VLSM (Variable Length Subnet Mask) technique. + return ".".join(map(str, mask_octets)) + +def vlsm(base_ip: str, subnets: str) -> str: + """Implement the VLSM (Variable Length Subnet Mask) technique. + + Args: + base_ip (str): The base IPv4 network address (e.g., "192.168.1.0"). + subnets (str): A list of integers representing the number of required hosts + for each subnet. + + Returns: + str: A list of dictionaries containing subnet details: network, mask, broadcast, + and usable range. - :param base_ip: The base IPv4 network address (e.g., "192.168.1.0"). - :param subnets: A list of integers representing the number of required hosts for each subnet. - :return: A list of dictionaries containing subnet details: network, mask, broadcast, and usable range. """ if not is_valid(base_ip): return "Invalid base IP address" - + # Check if any subnet requires more than 255 machines - if any(hosts > 255 for hosts in subnets): + if any(hosts > 255 for hosts in subnets): # noqa: PLR2004 return "Error: Each subnet can only accommodate up to 255 machines." # Sort subnets in descending order (to allocate the largest first) @@ -176,12 +214,30 @@ def vlsm(base_ip, subnets): results = [] # Helper functions to convert between IP addresses and integers - def ip_to_int(ip): - octets = list(map(int, ip.split('.'))) + def ip_to_int(ip: str) -> int: + """Convert an IPv4 address to an integer. + + Args: + ip (str): ipv4 address + + Returns: + int: The integer representation of the IPv4 address. + + """ + octets = list(map(int, ip.split("."))) return (octets[0] << 24) + (octets[1] << 16) + (octets[2] << 8) + octets[3] - def int_to_ip(ip_int): - return '.'.join([str((ip_int >> (8 * i)) & 255) for i in range(3, -1, -1)]) + def int_to_ip(ip_int: int) -> str: + """Convert an integer to an IPv4 address. + + Args: + ip_int (int): Integer representation of an IPv4 address. + + Returns: + str: IPv4 address. + + """ + return ".".join([str((ip_int >> (8 * i)) & 255) for i in range(3, -1, -1)]) current_ip_int = ip_to_int(base_ip) @@ -197,19 +253,19 @@ def int_to_ip(ip_int): broadcast_ip = network_ip + (1 << (32 - prefix)) - 1 # Check for valid IPv4 address range - if broadcast_ip > 0xFFFFFFFF: - raise ValueError("Out of IPv4 address range") + if broadcast_ip > 0xFFFFFFFF: # noqa: PLR2004 + msg = "Out of IPv4 address range" + raise ValueError(msg) # Append the results to the list results.append({ - 'network': int_to_ip(network_ip), - 'mask': get_ipv4_mask(f"0.0.0.0/{prefix}"), - 'broadcast': int_to_ip(broadcast_ip), - 'range': f"{int_to_ip(network_ip + 1)} - {int_to_ip(broadcast_ip - 1)}" + "network": int_to_ip(network_ip), + "mask": get_ipv4_mask(f"0.0.0.0/{prefix}"), + "broadcast": int_to_ip(broadcast_ip), + "range": f"{int_to_ip(network_ip + 1)} - {int_to_ip(broadcast_ip - 1)}", }) # Move to the next subnet's network address current_ip_int = broadcast_ip + 1 return results -