[CXF-5697] Some minor updates for WHICH_JAR
[cxf.git] / services / sts / sts-core / src / main / java / org / apache / cxf / sts / token / provider / jwt / DefaultJWTClaimsProvider.java
1 /**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.cxf.sts.token.provider.jwt;
20
21 import java.security.Principal;
22 import java.text.ParseException;
23 import java.util.ArrayList;
24 import java.util.Date;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.UUID;
28 import java.util.logging.Logger;
29
30 import javax.security.auth.x500.X500Principal;
31
32 import org.apache.cxf.common.logging.LogUtils;
33 import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
34 import org.apache.cxf.sts.STSPropertiesMBean;
35 import org.apache.cxf.sts.claims.ClaimsUtils;
36 import org.apache.cxf.sts.claims.ProcessedClaim;
37 import org.apache.cxf.sts.claims.ProcessedClaimCollection;
38 import org.apache.cxf.sts.request.Lifetime;
39 import org.apache.cxf.sts.request.Participants;
40 import org.apache.cxf.sts.request.ReceivedToken;
41 import org.apache.cxf.sts.request.ReceivedToken.STATE;
42 import org.apache.cxf.sts.token.provider.TokenProviderParameters;
43 import org.apache.cxf.sts.token.provider.TokenProviderUtils;
44 import org.apache.cxf.ws.security.sts.provider.STSException;
45 import org.apache.wss4j.dom.util.XmlSchemaDateFormat;
46
47 /**
48 * A default implementation to create a JWTClaims object. The Subject name is the name
49 * of the current principal.
50 */
51 public class DefaultJWTClaimsProvider implements JWTClaimsProvider {
52
53 public static final long DEFAULT_MAX_LIFETIME = 60L * 60L * 12L;
54
55 private static final Logger LOG = LogUtils.getL7dLogger(DefaultJWTClaimsProvider.class);
56 private boolean useX500CN;
57
58 private long lifetime = 60L * 30L;
59 private long maxLifetime = DEFAULT_MAX_LIFETIME;
60 private boolean failLifetimeExceedance = true;
61 private boolean acceptClientLifetime;
62 private long futureTimeToLive = 60L;
63
64 /**
65 * Get a JwtClaims object.
66 */
67 public JwtClaims getJwtClaims(JWTClaimsProviderParameters jwtClaimsProviderParameters) {
68
69 JwtClaims claims = new JwtClaims();
70 claims.setSubject(getSubjectName(jwtClaimsProviderParameters));
71 claims.setTokenId(UUID.randomUUID().toString());
72
73 // Set the Issuer
74 String issuer = jwtClaimsProviderParameters.getIssuer();
75 if (issuer == null) {
76 STSPropertiesMBean stsProperties = jwtClaimsProviderParameters.getProviderParameters().getStsProperties();
77 claims.setIssuer(stsProperties.getIssuer());
78 } else {
79 claims.setIssuer(issuer);
80 }
81
82 handleWSTrustClaims(jwtClaimsProviderParameters, claims);
83
84 handleConditions(jwtClaimsProviderParameters, claims);
85
86 handleAudienceRestriction(jwtClaimsProviderParameters, claims);
87
88 return claims;
89 }
90
91 protected String getSubjectName(JWTClaimsProviderParameters jwtClaimsProviderParameters) {
92 Principal principal = getPrincipal(jwtClaimsProviderParameters);
93 if (principal == null) {
94 LOG.fine("Error in getting principal");
95 throw new STSException("Error in getting principal", STSException.REQUEST_FAILED);
96 }
97
98 String subjectName = principal.getName();
99 if (principal instanceof X500Principal) {
100 // Just use the "cn" instead of the entire DN
101 try {
102 String principalName = principal.getName();
103 int index = principalName.indexOf('=');
104 principalName = principalName.substring(index + 1, principalName.indexOf(',', index));
105 subjectName = principalName;
106 } catch (Throwable ex) {
107 subjectName = principal.getName();
108 //Ignore, not X500 compliant thus use the whole string as the value
109 }
110 }
111
112 return subjectName;
113 }
114
115 /**
116 * Get the Principal (which is used as the Subject). By default, we check the following (in order):
117 * - A valid OnBehalfOf principal
118 * - A valid ActAs principal
119 * - A valid principal associated with a token received as ValidateTarget
120 * - The principal associated with the request. We don't need to check to see if it is "valid" here, as it
121 * is not parsed by the STS (but rather the WS-Security layer).
122 */
123 protected Principal getPrincipal(JWTClaimsProviderParameters jwtClaimsProviderParameters) {
124 TokenProviderParameters providerParameters = jwtClaimsProviderParameters.getProviderParameters();
125
126 Principal principal = null;
127 //TokenValidator in IssueOperation has validated the ReceivedToken
128 //if validation was successful, the principal was set in ReceivedToken
129 if (providerParameters.getTokenRequirements().getOnBehalfOf() != null) {
130 ReceivedToken receivedToken = providerParameters.getTokenRequirements().getOnBehalfOf();
131 if (receivedToken.getState().equals(STATE.VALID)) {
132 principal = receivedToken.getPrincipal();
133 }
134 } else if (providerParameters.getTokenRequirements().getActAs() != null) {
135 ReceivedToken receivedToken = providerParameters.getTokenRequirements().getActAs();
136 if (receivedToken.getState().equals(STATE.VALID)) {
137 principal = receivedToken.getPrincipal();
138 }
139 } else if (providerParameters.getTokenRequirements().getValidateTarget() != null) {
140 ReceivedToken receivedToken = providerParameters.getTokenRequirements().getValidateTarget();
141 if (receivedToken.getState().equals(STATE.VALID)) {
142 principal = receivedToken.getPrincipal();
143 }
144 } else {
145 principal = providerParameters.getPrincipal();
146 }
147
148 return principal;
149 }
150
151 protected void handleWSTrustClaims(JWTClaimsProviderParameters jwtClaimsProviderParameters, JwtClaims claims) {
152 TokenProviderParameters providerParameters = jwtClaimsProviderParameters.getProviderParameters();
153
154 // Handle Claims
155 ProcessedClaimCollection retrievedClaims = ClaimsUtils.processClaims(providerParameters);
156 if (retrievedClaims != null) {
157 Iterator<ProcessedClaim> claimIterator = retrievedClaims.iterator();
158 while (claimIterator.hasNext()) {
159 ProcessedClaim claim = claimIterator.next();
160 if (claim.getClaimType() != null && claim.getValues() != null && !claim.getValues().isEmpty()) {
161 Object claimValues = claim.getValues();
162 if (claim.getValues().size() == 1) {
163 claimValues = claim.getValues().get(0);
164 }
165 claims.setProperty(claim.getClaimType().toString(), claimValues);
166 }
167 }
168 }
169 }
170
171 protected void handleConditions(JWTClaimsProviderParameters jwtClaimsProviderParameters, JwtClaims claims) {
172 TokenProviderParameters providerParameters = jwtClaimsProviderParameters.getProviderParameters();
173
174 Date currentDate = new Date();
175 long currentTimeInSeconds = currentDate.getTime() / 1000L;
176
177 // Set the defaults first
178 claims.setIssuedAt(currentTimeInSeconds);
179 claims.setNotBefore(currentTimeInSeconds);
180 claims.setExpiryTime(currentTimeInSeconds + lifetime);
181
182 Lifetime tokenLifetime = providerParameters.getTokenRequirements().getLifetime();
183 if (lifetime > 0 && acceptClientLifetime && tokenLifetime != null
184 && tokenLifetime.getCreated() != null && tokenLifetime.getExpires() != null) {
185 try {
186 XmlSchemaDateFormat fmt = new XmlSchemaDateFormat();
187 Date creationTime = fmt.parse(tokenLifetime.getCreated());
188 Date expirationTime = fmt.parse(tokenLifetime.getExpires());
189 if (creationTime == null || expirationTime == null) {
190 LOG.fine("Error in parsing Timestamp Created or Expiration Strings");
191 throw new STSException(
192 "Error in parsing Timestamp Created or Expiration Strings",
193 STSException.INVALID_TIME
194 );
195 }
196
197 // Check to see if the created time is in the future
198 Date validCreation = new Date();
199 long currentTime = validCreation.getTime();
200 if (futureTimeToLive > 0) {
201 validCreation.setTime(currentTime + futureTimeToLive * 1000L);
202 }
203 if (creationTime.after(validCreation)) {
204 LOG.fine("The Created Time is too far in the future");
205 throw new STSException("The Created Time is too far in the future", STSException.INVALID_TIME);
206 }
207
208 long requestedLifetime = expirationTime.getTime() - creationTime.getTime();
209 if (requestedLifetime > (getMaxLifetime() * 1000L)) {
210 StringBuilder sb = new StringBuilder();
211 sb.append("Requested lifetime [").append(requestedLifetime / 1000L);
212 sb.append(" sec] exceed configured maximum lifetime [").append(getMaxLifetime());
213 sb.append(" sec]");
214 LOG.warning(sb.toString());
215 if (isFailLifetimeExceedance()) {
216 throw new STSException("Requested lifetime exceeds maximum lifetime",
217 STSException.INVALID_TIME);
218 } else {
219 expirationTime.setTime(creationTime.getTime() + (getMaxLifetime() * 1000L));
220 }
221 }
222
223 long creationTimeInSeconds = creationTime.getTime() / 1000L;
224 claims.setIssuedAt(creationTimeInSeconds);
225 claims.setNotBefore(creationTimeInSeconds);
226 claims.setExpiryTime(expirationTime.getTime() / 1000L);
227 } catch (ParseException e) {
228 LOG.warning("Failed to parse life time element: " + e.getMessage());
229 }
230 }
231 }
232
233 /**
234 * Set the audience restriction claim. The Audiences are from an AppliesTo address, and the wst:Participants
235 * (if either exist).
236 */
237 protected void handleAudienceRestriction(
238 JWTClaimsProviderParameters jwtClaimsProviderParameters, JwtClaims claims
239 ) {
240 TokenProviderParameters providerParameters = jwtClaimsProviderParameters.getProviderParameters();
241
242 List<String> audiences = new ArrayList<String>();
243 String appliesToAddress = providerParameters.getAppliesToAddress();
244 if (appliesToAddress != null) {
245 audiences.add(appliesToAddress);
246 }
247
248 Participants participants = providerParameters.getTokenRequirements().getParticipants();
249 if (participants != null) {
250 String address = TokenProviderUtils.extractAddressFromParticipantsEPR(participants.getPrimaryParticipant());
251 if (address != null) {
252 audiences.add(address);
253 }
254
255 if (participants.getParticipants() != null) {
256 for (Object participant : participants.getParticipants()) {
257 if (participant != null) {
258 address = TokenProviderUtils.extractAddressFromParticipantsEPR(participant);
259 if (address != null) {
260 audiences.add(address);
261 }
262 }
263 }
264 }
265 }
266 if (!audiences.isEmpty()) {
267 claims.setAudiences(audiences);
268 }
269
270 }
271
272 public boolean isUseX500CN() {
273 return useX500CN;
274 }
275
276 public void setUseX500CN(boolean useX500CN) {
277 this.useX500CN = useX500CN;
278 }
279
280 /**
281 * Get how long (in seconds) a client-supplied Created Element is allowed to be in the future.
282 * The default is 60 seconds to avoid common problems relating to clock skew.
283 */
284 public long getFutureTimeToLive() {
285 return futureTimeToLive;
286 }
287
288 /**
289 * Set how long (in seconds) a client-supplied Created Element is allowed to be in the future.
290 * The default is 60 seconds to avoid common problems relating to clock skew.
291 */
292 public void setFutureTimeToLive(long futureTimeToLive) {
293 this.futureTimeToLive = futureTimeToLive;
294 }
295
296 /**
297 * Set the default lifetime in seconds for issued JWT tokens
298 * @param default lifetime in seconds
299 */
300 public void setLifetime(long lifetime) {
301 this.lifetime = lifetime;
302 }
303
304 /**
305 * Get the default lifetime in seconds for issued JWT token where requestor
306 * doesn't specify a lifetime element
307 * @return the lifetime in seconds
308 */
309 public long getLifetime() {
310 return lifetime;
311 }
312
313 /**
314 * Set the maximum lifetime in seconds for issued JWT tokens
315 * @param maximum lifetime in seconds
316 */
317 public void setMaxLifetime(long maxLifetime) {
318 this.maxLifetime = maxLifetime;
319 }
320
321 /**
322 * Get the maximum lifetime in seconds for issued JWT token
323 * if requestor specifies lifetime element
324 * @return the maximum lifetime in seconds
325 */
326 public long getMaxLifetime() {
327 return maxLifetime;
328 }
329
330 /**
331 * Is client lifetime element accepted
332 * Default: false
333 */
334 public boolean isAcceptClientLifetime() {
335 return this.acceptClientLifetime;
336 }
337
338 /**
339 * Set whether client lifetime is accepted
340 */
341 public void setAcceptClientLifetime(boolean acceptClientLifetime) {
342 this.acceptClientLifetime = acceptClientLifetime;
343 }
344
345 /**
346 * If requested lifetime exceeds shall it fail (default)
347 * or overwrite with maximum lifetime
348 */
349 public boolean isFailLifetimeExceedance() {
350 return this.failLifetimeExceedance;
351 }
352
353 /**
354 * If requested lifetime exceeds shall it fail (default)
355 * or overwrite with maximum lifetime
356 */
357 public void setFailLifetimeExceedance(boolean failLifetimeExceedance) {
358 this.failLifetimeExceedance = failLifetimeExceedance;
359 }
360
361 }