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.pipeline;
017
018import de.cuioss.http.security.core.HttpSecurityValidator;
019import de.cuioss.http.security.core.ValidationType;
020import de.cuioss.http.security.exceptions.UrlSecurityException;
021import de.cuioss.http.security.monitoring.SecurityEventCounter;
022import lombok.Getter;
023import lombok.RequiredArgsConstructor;
024import org.jspecify.annotations.Nullable;
025
026import java.util.List;
027import java.util.Optional;
028
029/**
030 * Abstract base class for validation pipelines that provides common validation logic.
031 *
032 * <p>This class implements the standard pipeline validation pattern where multiple
033 * validation stages are executed sequentially with early termination on security violations.</p>
034 *
035 * <h3>Key Features</h3>
036 * <ul>
037 *   <li><strong>Sequential Processing</strong> - Each stage processes the output of the previous stage</li>
038 *   <li><strong>Early Termination</strong> - Pipeline stops on first security violation</li>
039 *   <li><strong>Event Tracking</strong> - Security violations are tracked via SecurityEventCounter</li>
040 *   <li><strong>Exception Enhancement</strong> - Exceptions are re-thrown with correct validation type</li>
041 * </ul>
042 *
043 * @since 1.0
044 */
045@RequiredArgsConstructor
046@Getter
047public abstract class AbstractValidationPipeline implements HttpSecurityValidator {
048
049    /**
050     * The ordered list of validation stages to execute.
051     */
052    protected final List<HttpSecurityValidator> stages;
053
054    /**
055     * Counter for tracking security events.
056     */
057    protected final SecurityEventCounter eventCounter;
058
059    /**
060     * Returns the validation type handled by this pipeline.
061     *
062     * @return The validation type for this pipeline
063     */
064    public abstract ValidationType getValidationType();
065
066    @Override
067    public Optional<String> validate(@Nullable String value) throws UrlSecurityException {
068        if (value == null) {
069            return Optional.empty();
070        }
071
072        String result = value;
073
074        // Sequential execution with early termination
075        for (HttpSecurityValidator stage : stages) {
076            try {
077                Optional<String> stageResult = stage.validate(result);
078                if (stageResult.isEmpty()) {
079                    return Optional.empty();
080                }
081                result = stageResult.get();
082            } catch (UrlSecurityException e) {
083                // Track security event
084                eventCounter.increment(e.getFailureType());
085
086                // Re-throw with correct validation type
087                throw UrlSecurityException.builder()
088                        .failureType(e.getFailureType())
089                        .validationType(getValidationType())
090                        .originalInput(value) // Use original input, not current result
091                        .sanitizedInput(e.getSanitizedInput().orElse(null))
092                        .detail(e.getDetail().orElse("Validation failed"))
093                        .cause(e.getCause())
094                        .build();
095            }
096        }
097
098        return Optional.of(result);
099    }
100}