001/* 002 * Copyright © 2025 CUI-OpenSource-Software (info@cuioss.de) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package de.cuioss.http.security.data; 017 018import de.cuioss.http.security.core.ValidationType; 019import org.jspecify.annotations.Nullable; 020 021/** 022 * Immutable record representing a URL query parameter with name and value. 023 * 024 * <p>This record encapsulates the key-value pair structure of URL query parameters, 025 * providing a type-safe way to handle parameter data in HTTP security validation.</p> 026 * 027 * <h3>Design Principles</h3> 028 * <ul> 029 * <li><strong>Immutability</strong> - All fields are final and the record cannot be modified</li> 030 * <li><strong>Type Safety</strong> - Strongly typed representation of parameter data</li> 031 * <li><strong>Null Safety</strong> - Explicit handling of null values with clear semantics</li> 032 * <li><strong>Value Semantics</strong> - Records provide automatic equals/hashCode/toString</li> 033 * </ul> 034 * 035 * <h3>Usage Examples</h3> 036 * <pre> 037 * // Create a parameter 038 * URLParameter param = new URLParameter("userId", "12345"); 039 * 040 * // Access components 041 * String name = param.name(); // "userId" 042 * String value = param.value(); // "12345" 043 * 044 * // Use in validation 045 * validator.validate(param.name(), ValidationType.PARAMETER_NAME); 046 * validator.validate(param.value(), ValidationType.PARAMETER_VALUE); 047 * 048 * // Parameters are value objects 049 * URLParameter param2 = new URLParameter("userId", "12345"); 050 * assert param.equals(param2); // true 051 * </pre> 052 * 053 * <h3>Null Handling</h3> 054 * <p>Both name and value can be null to represent edge cases in HTTP parsing, 055 * though null parameter names are typically invalid in well-formed URLs.</p> 056 * 057 * <h3>Security Considerations</h3> 058 * <p>This record is a simple data container. Security validation should be applied 059 * to the name and value components separately using appropriate validators for 060 * {@link ValidationType#PARAMETER_NAME} and {@link ValidationType#PARAMETER_VALUE}.</p> 061 * 062 * Implements: Task B3 from HTTP verification specification 063 * 064 * @param name The parameter name (e.g., "userId", "page", "filter") 065 * @param value The parameter value (e.g., "12345", "admin", "active") 066 * 067 * @since 1.0 068 * @see ValidationType#PARAMETER_NAME 069 * @see ValidationType#PARAMETER_VALUE 070 */ 071public record URLParameter(@Nullable String name, @Nullable String value) { 072 073 /** 074 * Creates a URLParameter with empty value. 075 * Useful for parameters that appear without values (e.g., "?flag" instead of "?flag=value"). 076 * 077 * @param name The parameter name, should not be null 078 * @return A URLParameter with the specified name and empty string value 079 */ 080 public static URLParameter withEmptyValue(String name) { 081 return new URLParameter(name, ""); 082 } 083 084 /** 085 * Checks if this parameter has a non-null, non-empty name. 086 * 087 * @return true if the name is not null and not empty 088 */ 089 public boolean hasName() { 090 return name != null && !name.isEmpty(); 091 } 092 093 /** 094 * Checks if this parameter has a non-null, non-empty value. 095 * 096 * @return true if the value is not null and not empty 097 */ 098 public boolean hasValue() { 099 return value != null && !value.isEmpty(); 100 } 101 102 /** 103 * Checks if this parameter represents a flag (has name but no meaningful value). 104 * A parameter is considered a flag if it has a name but the value is null or empty. 105 * 106 * @return true if this appears to be a flag parameter 107 */ 108 public boolean isFlag() { 109 return hasName() && (value == null || value.isEmpty()); 110 } 111 112 /** 113 * Returns the parameter name, or a default value if the name is null. 114 * 115 * @param defaultName The default name to return if name is null 116 * @return The parameter name or the default 117 */ 118 public String nameOrDefault(String defaultName) { 119 return name != null ? name : defaultName; 120 } 121 122 /** 123 * Returns the parameter value, or a default value if the value is null. 124 * 125 * @param defaultValue The default value to return if value is null 126 * @return The parameter value or the default 127 */ 128 public String valueOrDefault(String defaultValue) { 129 return value != null ? value : defaultValue; 130 } 131 132 /** 133 * Returns a string representation suitable for URL encoding. 134 * Note: This does not perform actual URL encoding - use appropriate 135 * encoding utilities for that purpose. 136 * 137 * @return A string in the format "name=value" or "name" for flag parameters 138 */ 139 public String toParameterString() { 140 if (name == null) { 141 return value != null ? "=" + value : ""; 142 } 143 if (value == null || value.isEmpty()) { 144 return name; 145 } 146 return name + "=" + value; 147 } 148 149 public URLParameter withName(String newName) { 150 return new URLParameter(newName, value); 151 } 152 153 public URLParameter withValue(String newValue) { 154 return new URLParameter(name, newValue); 155 } 156}