All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.helidon.security.SecurityResponse Maven / Gradle / Ivy

/*
 * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.helidon.security;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import io.helidon.common.Builder;
import io.helidon.common.CollectionsHelper;

/**
 * Response from security provider (and security Module).
 */
public abstract class SecurityResponse {
    private final Map> requestHeaders;
    private final Map> responseHeaders;
    private final SecurityStatus status;
    private final String description;
    private final Throwable throwable;
    private final int statusCode;

    SecurityResponse(SecurityResponseBuilder, ?> builder) {
        this.status = builder.status;
        this.description = builder.description;
        this.throwable = builder.throwable;
        this.requestHeaders = builder.requestHeaders;
        this.responseHeaders = builder.responseHeaders;
        this.statusCode = builder.statusCode;
    }

    /**
     * Synchronize a completion stage.
     *
     * @param stage future response
     * @param    type the future response will provide
     * @return instance the future returns
     * @throws SecurityException in case of timeout, interrupted call or exception during future processing
     */
    static  T get(CompletionStage stage) {
        try {
            // since java 9 this method is not optional, so we can safely call it
            return stage.toCompletableFuture().get(60, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new SecurityException("Interrupted while waiting for completion stage to complete", e);
        } catch (ExecutionException e) {
            throw new SecurityException("Failure while executing asynchronous security", e);
        } catch (TimeoutException e) {
            throw new SecurityException("Timed out after waiting for completion stage to complete", e);
        }
    }

    /**
     * Status of this response.
     *
     * @return SecurityStatus as the provider responded
     */
    public SecurityStatus status() {
        return status;
    }

    /**
     * Status code (uses HTTP status codes for mapping).
     *
     * @return HTTP status code the provider wants to use, or empty if not set
     */
    public OptionalInt statusCode() {
        return (statusCode == -1 ? OptionalInt.empty() : OptionalInt.of(statusCode));
    }

    /**
     * Description of current security status. Should be provided by security providers mostly for failure cases.
     *
     * @return Description of current status (optional)
     */
    public Optional description() {
        return Optional.ofNullable(description);
    }

    /**
     * Get underlying throwable causing a failure state (if such happened).
     *
     * @return Exception causing current failure (optional)
     */
    public Optional throwable() {
        return Optional.ofNullable(throwable);
    }

    /**
     * Get new request headers to be used. These may be additional header, replacement headers or "clearing" headers (in case
     * the value is empty list).
     *
     * @return Map of headers to merge with existing headers
     */
    public Map> requestHeaders() {
        return requestHeaders;
    }

    /**
     * Get new response headers to be used. These may be additional header, replacement headers or "clearing" headers (in case
     * the value is empty list).
     *
     * @return Map of headers to merge with existing headers
     */
    public Map> responseHeaders() {
        return responseHeaders;
    }

    @Override
    public String toString() {
        return "SecurityResponse{"
                + "status=" + status
                + ", description='" + description + '\''
                + ", statusCode=" + statusCode
                + '}';
    }

    /**
     * Status of a security operation.
     */
    public enum SecurityStatus {
        /**
         * Indicates that the message processing by the security component
         * was successful and that the runtime is to proceed with its normal
         * processing of the resulting message.
         * Example: authentication successful, continue with request processing.
         */
        SUCCESS(true),
        /**
         * Succeeded and provider did everything to be done.
         * Finish processing (do nothing more in current flow).
         *
         * The provider should have:
         * 
    *
  • Updated entity (through {@link SecurityRequest#responseEntity()}
  • *
  • Updated headers (through {@link SecurityResponseBuilder#responseHeader(String, String)}
  • *
  • Updated status code (through {@link SecurityResponseBuilder#statusCode(int)}
  • *
*/ SUCCESS_FINISH(true), /** * Indicates that the message processing by the security module * was NOT successful. * Example: authorization failure */ FAILURE, /** * Failed and provider did everything to be done. Finish processing (do nothing more in current flow). */ FAILURE_FINISH, /** * Cannot process, not an error. */ ABSTAIN; private final boolean success; SecurityStatus() { this(false); } SecurityStatus(boolean success) { this.success = success; } public boolean isSuccess() { return success; } } /** * Builder for security response. * * @param Type of security response to build */ abstract static class SecurityResponseBuilder, B> implements Builder { private final Map> requestHeaders = new HashMap<>(); private final Map> responseHeaders = new HashMap<>(); private final T myInstance; private SecurityStatus status = SecurityStatus.SUCCESS; private String description; private Throwable throwable; private int statusCode = -1; @SuppressWarnings("unchecked") SecurityResponseBuilder() { this.myInstance = (T) this; } /** * Set a status code for failed statuses. This is expected to use HTTP status * codes. If an integration is done with a non-http protocol, you must map these * status codes appropriately. * * @param statusCode HTTP status code * @return updated builder instance */ public T statusCode(int statusCode) { this.statusCode = statusCode; return myInstance; } /** * Set security status of this security response. * * @param status Status to set * @return updated builder instance */ public T status(SecurityStatus status) { this.status = status; return myInstance; } /** * Set description of this security response failure. * * @param description Description to provide to called in case of failed security, or null if no information can be * provided * @return updated builder instance */ public T description(String description) { this.description = description; return myInstance; } /** * Set throwable causing failure of the security request. * * @param exception Exception that caused failure * @return updated builder instance */ public T throwable(Throwable exception) { this.throwable = exception; return myInstance; } /** * Set additional/replacement headers for request. * * @param headers map with headers * @return updated builder instance */ public T requestHeaders(Map> headers) { this.requestHeaders.clear(); this.requestHeaders.putAll(headers); return myInstance; } /** * Add a single-value header. Note that if method {@link #requestHeaders(Map)} is called after * this method, it will remove changes by this method. * * @param header header name * @param value header value * @return this instance */ public T requestHeader(String header, String value) { requestHeaders.put(header, CollectionsHelper.listOf(value)); return myInstance; } /** * Add a multi-value header. Note that if method {@link #requestHeaders(Map)} is called after * this method, it may remove changes by this method. * * @param header header name * @param values header values * @return this instance */ public T requestHeader(String header, List values) { requestHeaders.put(header, values); return myInstance; } /** * Set additional/replacement headers for request. * * @param headers map with headers * @return updated builder instance */ public T responseHeaders(Map> headers) { this.responseHeaders.clear(); this.responseHeaders.putAll(headers); return myInstance; } /** * Add a single-value header. Note that if method {@link #responseHeaders(Map)} is called after * this method, it will remove changes by this method. * * @param header header name * @param value header value * @return this instance */ public T responseHeader(String header, String value) { responseHeaders.put(header, CollectionsHelper.listOf(value)); return myInstance; } /** * Add a multi-value header. Note that if method {@link #responseHeaders(Map)} is called after * this method, it may remove changes by this method. * * @param header header name * @param values header values * @return this instance */ public T responseHeader(String header, List values) { responseHeaders.put(header, values); return myInstance; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy