1
|
|
|
package org.apereo.cas.token; |
2
|
|
|
|
3
|
|
|
import com.nimbusds.jwt.JWTClaimsSet; |
4
|
|
|
import com.nimbusds.jwt.PlainJWT; |
5
|
|
|
import net.minidev.json.JSONObject; |
6
|
|
|
import org.apereo.cas.CipherExecutor; |
7
|
|
|
import org.apereo.cas.authentication.Authentication; |
8
|
|
|
import org.apereo.cas.authentication.principal.Service; |
9
|
|
|
import org.apereo.cas.ticket.ExpirationPolicy; |
10
|
|
|
import org.apereo.cas.ticket.TicketGrantingTicket; |
11
|
|
|
import org.apereo.cas.util.DateTimeUtils; |
12
|
|
|
import org.hjson.JsonValue; |
13
|
|
|
import org.hjson.Stringify; |
14
|
|
|
import org.jasig.cas.client.validation.AbstractUrlBasedTicketValidator; |
15
|
|
|
import org.jasig.cas.client.validation.Assertion; |
16
|
|
|
import org.jasig.cas.client.validation.TicketValidator; |
17
|
|
|
import org.slf4j.Logger; |
18
|
|
|
import org.slf4j.LoggerFactory; |
19
|
|
|
|
20
|
|
|
import java.time.ZonedDateTime; |
21
|
|
|
import java.util.Date; |
22
|
|
|
import java.util.LinkedHashMap; |
23
|
|
|
import java.util.Map; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* This is {@link JWTTokenTicketBuilder}. |
27
|
|
|
* |
28
|
|
|
* @author Misagh Moayyed |
29
|
|
|
* @since 5.2.0 |
30
|
|
|
*/ |
31
|
|
|
public class JWTTokenTicketBuilder implements TokenTicketBuilder { |
32
|
|
|
private static final Logger LOGGER = LoggerFactory.getLogger(JWTTokenTicketBuilder.class); |
33
|
|
|
|
34
|
|
|
private final TicketValidator ticketValidator; |
35
|
|
|
private final String casSeverPrefix; |
36
|
|
|
private final CipherExecutor<String, String> tokenCipherExecutor; |
37
|
|
|
private final ExpirationPolicy expirationPolicy; |
38
|
|
|
|
39
|
|
|
public JWTTokenTicketBuilder(final AbstractUrlBasedTicketValidator ticketValidator, |
40
|
|
|
final String casSeverPrefix, |
41
|
|
|
final CipherExecutor<String, String> tokenCipherExecutor, |
42
|
|
|
final ExpirationPolicy expirationPolicy) { |
43
|
|
|
this.ticketValidator = ticketValidator; |
44
|
|
|
this.casSeverPrefix = casSeverPrefix; |
45
|
|
|
this.tokenCipherExecutor = tokenCipherExecutor; |
46
|
|
|
this.expirationPolicy = expirationPolicy; |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
@Override |
50
|
|
|
public String build(final String serviceTicketId, final Service service) { |
51
|
|
|
try { |
52
|
|
|
final Assertion assertion = this.ticketValidator.validate(serviceTicketId, service.getId()); |
53
|
|
|
final Map<String, Object> attributes = new LinkedHashMap<>(assertion.getAttributes()); |
54
|
|
|
attributes.putAll(assertion.getPrincipal().getAttributes()); |
55
|
|
|
|
56
|
|
|
final Date validUntilDate; |
57
|
|
|
if (assertion.getValidUntilDate() != null) { |
58
|
|
|
validUntilDate = assertion.getValidUntilDate(); |
59
|
|
|
} else { |
60
|
|
|
final ZonedDateTime dt = ZonedDateTime.now().plusSeconds(expirationPolicy.getTimeToLive()); |
61
|
|
|
validUntilDate = DateTimeUtils.dateOf(dt); |
62
|
|
|
} |
63
|
|
|
return buildJwt(serviceTicketId, service.getId(), assertion.getAuthenticationDate(), |
64
|
|
|
assertion.getPrincipal().getName(), validUntilDate, attributes); |
65
|
|
|
} catch (final Exception e) { |
66
|
|
|
throw new RuntimeException(e.getMessage(), e); |
67
|
|
|
} |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
@Override |
71
|
|
|
public String build(final TicketGrantingTicket ticketGrantingTicket) { |
72
|
|
|
try { |
73
|
|
|
final Authentication authentication = ticketGrantingTicket.getAuthentication(); |
74
|
|
|
final Map<String, Object> attributes = new LinkedHashMap<>(authentication.getAttributes()); |
75
|
|
|
attributes.putAll(authentication.getPrincipal().getAttributes()); |
76
|
|
|
|
77
|
|
|
final ZonedDateTime dt = ZonedDateTime.now().plusSeconds(expirationPolicy.getTimeToLive()); |
78
|
|
|
final Date validUntilDate = DateTimeUtils.dateOf(dt); |
79
|
|
|
return buildJwt(ticketGrantingTicket.getId(), casSeverPrefix, |
80
|
|
|
DateTimeUtils.dateOf(ticketGrantingTicket.getCreationTime()), |
81
|
|
|
authentication.getPrincipal().getId(), |
82
|
|
|
validUntilDate, attributes); |
83
|
|
|
} catch (final Exception e) { |
84
|
|
|
throw new RuntimeException(e.getMessage(), e); |
85
|
|
|
} |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
private String buildJwt(final String jwtId, final String audience, |
89
|
|
|
final Date issueDate, final String subject, |
90
|
|
|
final Date validUntilDate, final Map<String, Object> attributes) { |
91
|
|
|
final JWTClaimsSet.Builder claims = |
92
|
|
|
new JWTClaimsSet.Builder() |
93
|
|
|
.audience(audience) |
94
|
|
|
.issuer(casSeverPrefix) |
95
|
|
|
.jwtID(jwtId) |
96
|
|
|
.issueTime(issueDate) |
97
|
|
|
.subject(subject); |
98
|
|
|
|
99
|
|
|
attributes.forEach(claims::claim); |
100
|
|
|
claims.expirationTime(validUntilDate); |
101
|
|
|
|
102
|
|
|
final JWTClaimsSet claimsSet = claims.build(); |
103
|
|
|
final JSONObject object = claimsSet.toJSONObject(); |
104
|
|
|
|
105
|
|
|
final String jwtJson = object.toJSONString(); |
106
|
|
|
LOGGER.debug("Generated JWT [{}]", JsonValue.readJSON(jwtJson).toString(Stringify.FORMATTED)); |
107
|
|
|
if (tokenCipherExecutor.isEnabled()) { |
108
|
|
|
return tokenCipherExecutor.encode(jwtJson); |
109
|
|
|
} |
110
|
|
|
final String token = new PlainJWT(claimsSet).serialize(); |
111
|
|
|
return token; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
} |
115
|
|
|
|