View Javadoc
1   /*
2    * Copyright 2020 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.bremersee.actuator.security.authentication;
18  
19  import java.util.ArrayList;
20  import java.util.List;
21  import java.util.Set;
22  import java.util.TreeSet;
23  import java.util.stream.Collectors;
24  import javax.validation.constraints.NotEmpty;
25  import javax.validation.constraints.NotNull;
26  import lombok.EqualsAndHashCode;
27  import lombok.Getter;
28  import lombok.Setter;
29  import lombok.ToString;
30  import org.bremersee.security.authentication.AccessExpressionUtils;
31  import org.bremersee.security.authentication.AutoSecurityMode;
32  import org.bremersee.security.authentication.ClientCredentialsFlowProperties;
33  import org.bremersee.security.core.AuthorityConstants;
34  import org.springframework.boot.context.properties.ConfigurationProperties;
35  import org.springframework.util.StringUtils;
36  import org.springframework.validation.annotation.Validated;
37  
38  /**
39   * Actuator authentication and authorization properties.
40   *
41   * @author Christian Bremer
42   */
43  @ConfigurationProperties(prefix = "bremersee.actuator.auth")
44  @Getter
45  @Setter
46  @ToString
47  @EqualsAndHashCode
48  @Validated
49  public class ActuatorAuthProperties {
50  
51    /**
52     * Specifies the behaviour of the actuator security auto configuration.
53     */
54    @NotNull
55    private AutoSecurityMode enable = AutoSecurityMode.OTHER;
56  
57    /**
58     * The order of the actuator security auto configuration.
59     */
60    private int order = 61;
61  
62    /**
63     * Specifies whether cors should be enabled for the actuator endpoints or not.
64     */
65    private boolean enableCors = true;
66  
67    /**
68     * A list with unauthenticated actuator endpoints.
69     */
70    @NotNull
71    private List<Class<?>> unauthenticatedEndpoints = new ArrayList<>();
72  
73    /**
74     * The roles which can write to protected actuator endpoints. The role names normally start with {@code ROLE_}.
75     */
76    @NotNull
77    private List<String> adminRoles = new ArrayList<>();
78  
79    /**
80     * The roles which can read protected actuator endpoints. The role names normally start with {@code ROLE_}.
81     */
82    @NotNull
83    private List<String> roles = new ArrayList<>();
84  
85    /**
86     * The IP addresses which can access protected actuator endpoints without authentication.
87     */
88    @NotNull
89    private List<String> ipAddresses = new ArrayList<>();
90  
91    /**
92     * The JWK uri.
93     */
94    private String jwkSetUri;
95  
96    /**
97     * The JWS algorithm
98     */
99    @NotEmpty
100   private String jwsAlgorithm = "RS256";
101 
102   /**
103    * The issuer uri.
104    */
105   private String issuerUri;
106 
107   /**
108    * The json path in the JWT to the roles.
109    */
110   @NotEmpty
111   private String rolesJsonPath = "$.realm_access.roles";
112 
113   /**
114    * Specifies whether the roles value is a list (json array) or a simple string.
115    */
116   private boolean rolesValueList = true;
117 
118   /**
119    * The role value separator to use if the role value is a simple string.
120    */
121   @NotNull
122   private String rolesValueSeparator = " ";
123 
124   /**
125    * The role prefix to add.
126    */
127   @NotNull
128   private String rolePrefix = "ROLE_";
129 
130   /**
131    * The json path in the JWT to the user name.
132    */
133   @NotEmpty
134   private String nameJsonPath = "$.preferred_username";
135 
136   /**
137    * The password flow properties for the actuator endpoints.
138    */
139   @NotNull
140   private ActuatorPasswordFlow passwordFlow = new ActuatorPasswordFlow();
141 
142   /**
143    * Gets unauthenticated endpoints or defaults (these are the health and info endpoints).
144    *
145    * @return the unauthenticated endpoints
146    */
147   @NotNull
148   public List<Class<?>> unauthenticatedEndpointsOrDefaults() {
149     if (unauthenticatedEndpoints.isEmpty()) {
150       unauthenticatedEndpoints.add(
151           org.springframework.boot.actuate.health.HealthEndpoint.class);
152       unauthenticatedEndpoints.add(
153           org.springframework.boot.actuate.info.InfoEndpoint.class);
154     }
155     return unauthenticatedEndpoints;
156   }
157 
158   /**
159    * Roles or defaults.
160    *
161    * @return the roles
162    */
163   @NotNull
164   public Set<String> rolesOrDefaults() {
165     final TreeSet<String> roleSet = new TreeSet<>(roles);
166     if (roleSet.isEmpty()) {
167       roleSet.add(AuthorityConstants.ACTUATOR_ROLE_NAME);
168       roleSet.add(AuthorityConstants.ACTUATOR_ADMIN_ROLE_NAME);
169       roleSet.add(AuthorityConstants.ADMIN_ROLE_NAME);
170     }
171     return roleSet.stream()
172         .map(this::ensureRolePrefix)
173         .collect(Collectors.toSet());
174   }
175 
176   /**
177    * Admin roles or defaults.
178    *
179    * @return the admin roles
180    */
181   @NotNull
182   public Set<String> adminRolesOrDefaults() {
183     final TreeSet<String> roleSet = new TreeSet<>(adminRoles);
184     if (roleSet.isEmpty()) {
185       roleSet.add(AuthorityConstants.ACTUATOR_ADMIN_ROLE_NAME);
186       roleSet.add(AuthorityConstants.ADMIN_ROLE_NAME);
187     }
188     return roleSet.stream()
189         .map(this::ensureRolePrefix)
190         .collect(Collectors.toSet());
191   }
192 
193   /**
194    * Build access expression (SpEL) for actuator endpoints.
195    *
196    * @return the access expression (SpEL) for actuator endpoints
197    */
198   @NotNull
199   public String buildAccessExpression() {
200     return AccessExpressionUtils.hasAuthorityOrIpAddressExpr(
201         rolesOrDefaults(), null, ipAddresses);
202   }
203 
204   /**
205    * Build access expression (SpEL) for admin actuator endpoints.
206    *
207    * @return the access expression (SpEL) for admin actuator endpoints
208    */
209   @NotNull
210   public String buildAdminAccessExpression() {
211     return AccessExpressionUtils.hasAuthorityOrIpAddressExpr(adminRolesOrDefaults(), null, null);
212   }
213 
214   /**
215    * Ensure role prefix.
216    *
217    * @param role the role
218    * @return the role with prefix
219    */
220   @NotNull
221   public String ensureRolePrefix(@NotNull String role) {
222     final String prefix = rolePrefix.trim();
223     return StringUtils.hasText(prefix) && role.startsWith(prefix) ? role : prefix + role;
224   }
225 
226   /**
227    * OAuth2 password flow configuration properties.
228    */
229   @Getter
230   @Setter
231   @ToString(exclude = {"clientSecret"})
232   @EqualsAndHashCode(exclude = {"clientSecret"})
233   public static class ActuatorPasswordFlow implements ClientCredentialsFlowProperties {
234 
235     private String tokenEndpoint;
236 
237     private String clientId;
238 
239     private String clientSecret;
240   }
241 }