001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest.server.auth;
018
019
020import java.security.*;
021import java.util.*;
022
023
024import jakarta.servlet.http.*;
025import static org.apache.juneau.commons.utils.Shorts.*;
026
027/**
028 * An {@link HttpServletRequestWrapper} that surfaces the filter-resolved {@link Principal} and aggregated roles via the
029 * standard servlet API surface.
030 *
031 * <p>
032 * Created by {@link AuthFilter#doFilter} (standalone mode) and {@link AuthFilterChain#doFilter} (chain mode) after
033 * successful authentication. Overrides three servlet-API methods so downstream code &mdash; including
034 * {@link org.apache.juneau.rest.server.guard.RoleBasedRestGuard} and the {@link AuthArg}
035 * arg-resolver &mdash; sees the filter-resolved identity without any extra wiring:
036 * <ul>
037 *    <li>{@link #getUserPrincipal()} &mdash; returns the authenticated principal.
038 *    <li>{@link #isUserInRole(String)} &mdash; checks the aggregated role set.
039 *    <li>{@link #getRemoteUser()} &mdash; returns {@link Principal#getName()}.
040 *    <li>{@link #getAttribute(String)} &mdash; for the key {@code "juneau.principal"}, returns the
041 *       principal so the {@code @Auth} arg-resolver can find it.
042 * </ul>
043 *
044 * <h5 class='section'>See Also:</h5><ul>
045 *    <li class='jc'>{@link AuthFilter}
046 *    <li class='jc'>{@link AuthFilterChain}
047 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/AuthFilterFramework">AuthN Filter Framework — Servlet-Layer Authentication</a>
048 * </ul>
049 *
050 * @since 10.0.0
051 */
052@SuppressWarnings({
053   "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants
054})
055public class AuthenticatedRequestWrapper extends HttpServletRequestWrapper {
056
057   private final Principal principal;
058   private final Set<String> roles;
059
060   /**
061    * Constructor.
062    *
063    * @param req The underlying request. Must not be <jk>null</jk>.
064    * @param result The authentication result. Must not be <jk>null</jk>.
065    */
066   public AuthenticatedRequestWrapper(HttpServletRequest req, AuthResult result) {
067      super(reqnn("req", req));
068      reqnn("result", result);
069      this.principal = result.getPrincipal();
070      this.roles = new HashSet<>(result.getRoles());
071   }
072
073   /**
074    * Adds additional roles to the aggregated role set.
075    *
076    * <p>
077    * Called by {@link AuthFilterChain} when subsequent filters succeed and contribute additional roles.
078    *
079    * @param moreRoles The additional roles to add.
080    * @return This object (for chaining in the chain orchestrator).
081    */
082   AuthenticatedRequestWrapper withAdditionalRoles(Set<String> moreRoles) {
083      roles.addAll(moreRoles);
084      return this;
085   }
086
087   @Override /* Overridden from HttpServletRequest */
088   public Principal getUserPrincipal() {
089      return principal;
090   }
091
092   @Override /* Overridden from HttpServletRequest */
093   public boolean isUserInRole(String role) {
094      return roles.contains(role);
095   }
096
097   @Override /* Overridden from HttpServletRequest */
098   public String getRemoteUser() {
099      return principal.getName();
100   }
101
102   /**
103    * Intercepts {@code "juneau.principal"} so the {@code @Auth} arg-resolver can pick up the
104    * filter-stashed principal without any extra wiring.
105    *
106    * <p>
107    * All other attribute names delegate to the wrapped request.
108    */
109   @Override /* Overridden from HttpServletRequest */
110   public Object getAttribute(String name) {
111      if (eq(name, "juneau.principal"))
112         return principal;
113      return super.getAttribute(name);
114   }
115}