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
019import static org.apache.juneau.commons.utils.Shorts.*;
020
021import java.security.*;
022import java.util.*;
023
024/**
025 * Immutable value type returned by {@link AuthFilter#authenticate(jakarta.servlet.http.HttpServletRequest)} on success.
026 *
027 * <p>
028 * Carries the resolved {@link Principal} and the set of roles associated with the authenticated subject.
029 * The role set may be empty when no role claims are available (the principal is still valid).
030 *
031 * <h5 class='section'>See Also:</h5><ul>
032 *    <li class='jc'>{@link AuthFilter}
033 *    <li class='jc'>{@link AuthFilterChain}
034 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/AuthFilterFramework">AuthN Filter Framework — Servlet-Layer Authentication</a>
035 * </ul>
036 *
037 * @since 10.0.0
038 */
039@SuppressWarnings({
040   "java:S1192" // Duplicated "principal" literal is an assertion parameter name; a constant would obscure rather than clarify.
041})
042public final class AuthResult {
043
044   /**
045    * Controls how an {@link AuthResult} merges with results accumulated earlier in an authenticator chain.
046    *
047    * @since 10.0.0
048    */
049   public enum MergeMode {
050      /** Union this result's roles into the accumulated set; principal is optional (<jk>null</jk> keeps the inherited principal). */
051      ADD,
052      /** Discard everything accumulated so far; this result's principal + roles win.  Principal is required. */
053      REPLACE
054   }
055
056   private final Principal principal;   // may be null only in ADD mode
057   private final Set<String> roles;
058   private final MergeMode mode;
059
060   /**
061    * Creates an {@link AuthResult} with the specified principal and roles using {@link MergeMode#ADD} mode.
062    *
063    * @param principal The authenticated principal. Must not be <jk>null</jk>.
064    * @param roles The roles granted to this principal. May be empty.
065    * @return A new {@link AuthResult}.
066    */
067   public static AuthResult of(Principal principal, String... roles) {
068      reqnn("principal", principal);
069      return new AuthResult(principal, toSet(roles), MergeMode.ADD);
070   }
071
072   /**
073    * Creates an {@link AuthResult} with the specified principal and role set using {@link MergeMode#ADD} mode.
074    *
075    * @param principal The authenticated principal. Must not be <jk>null</jk>.
076    * @param roles The roles granted to this principal. May be <jk>null</jk> or empty.
077    * @return A new {@link AuthResult}.
078    */
079   public static AuthResult of(Principal principal, Set<String> roles) {
080      reqnn("principal", principal);
081      return new AuthResult(principal, toSet(roles), MergeMode.ADD);
082   }
083
084   /**
085    * Creates a roles-only {@link AuthResult} ({@link MergeMode#ADD} mode, <jk>null</jk> principal).
086    *
087    * <p>
088    * Use this to contribute additional roles without supplying a principal &mdash; the accumulated/inherited
089    * principal is kept.  This replaces the need for a separate roles-only authenticator type.
090    *
091    * @param roles The roles to contribute. May be empty.
092    * @return A new {@link AuthResult}.
093    */
094   public static AuthResult ofRoles(String... roles) {
095      return new AuthResult(null, toSet(roles), MergeMode.ADD);
096   }
097
098   /**
099    * Creates a roles-only {@link AuthResult} ({@link MergeMode#ADD} mode, <jk>null</jk> principal).
100    *
101    * @param roles The roles to contribute. May be <jk>null</jk> or empty.
102    * @return A new {@link AuthResult}.
103    */
104   public static AuthResult ofRoles(Collection<String> roles) {
105      return new AuthResult(null, toSet(roles), MergeMode.ADD);
106   }
107
108   /**
109    * Creates an {@link AuthResult} that {@link MergeMode#REPLACE replaces} any accumulated identity and roles.
110    *
111    * @param principal The authenticated principal. Must not be <jk>null</jk>.
112    * @param roles The roles granted to this principal. May be empty.
113    * @return A new {@link AuthResult}.
114    */
115   public static AuthResult replacing(Principal principal, String... roles) {
116      reqnn("principal", principal);
117      return new AuthResult(principal, toSet(roles), MergeMode.REPLACE);
118   }
119
120   /**
121    * Creates an {@link AuthResult} that {@link MergeMode#REPLACE replaces} any accumulated identity and roles.
122    *
123    * @param principal The authenticated principal. Must not be <jk>null</jk>.
124    * @param roles The roles granted to this principal. May be <jk>null</jk> or empty.
125    * @return A new {@link AuthResult}.
126    */
127   public static AuthResult replacing(Principal principal, Set<String> roles) {
128      reqnn("principal", principal);
129      return new AuthResult(principal, toSet(roles), MergeMode.REPLACE);
130   }
131
132   private static Set<String> toSet(String... roles) {
133      return roles == null ? Collections.emptySet() : new HashSet<>(Arrays.asList(roles));
134   }
135
136   private static Set<String> toSet(Collection<String> roles) {
137      return roles == null ? Collections.emptySet() : new HashSet<>(roles);
138   }
139
140   private AuthResult(Principal principal, Set<String> roles, MergeMode mode) {
141      this.principal = principal;
142      this.roles = u(roles);
143      this.mode = mode;
144   }
145
146   /**
147    * Returns the authenticated principal.
148    *
149    * @return The authenticated principal.  May be <jk>null</jk> for a roles-only ({@link MergeMode#ADD}) result.
150    */
151   public Principal getPrincipal() {
152      return principal;
153   }
154
155   /**
156    * Returns the merge mode for this result.
157    *
158    * @return The merge mode. Never <jk>null</jk>.
159    * @since 10.0.0
160    */
161   public MergeMode getMode() {
162      return mode;
163   }
164
165   /**
166    * Returns the unmodifiable set of roles granted to this principal.
167    *
168    * @return The role set. Never <jk>null</jk>; may be empty.
169    */
170   public Set<String> getRoles() {
171      return roles;
172   }
173}