001/******************************************************************************* 002 * Copyright 2014 The MITRE Corporation 003 * and the MIT Kerberos and Internet Trust Consortium 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * 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 ******************************************************************************/ 017/** 018 * 019 */ 020package org.mitre.oauth2.model; 021 022import java.text.ParseException; 023import java.util.Date; 024import java.util.HashMap; 025import java.util.Map; 026import java.util.Set; 027 028import javax.persistence.Basic; 029import javax.persistence.CascadeType; 030import javax.persistence.CollectionTable; 031import javax.persistence.Column; 032import javax.persistence.ElementCollection; 033import javax.persistence.Entity; 034import javax.persistence.FetchType; 035import javax.persistence.GeneratedValue; 036import javax.persistence.GenerationType; 037import javax.persistence.Id; 038import javax.persistence.JoinColumn; 039import javax.persistence.ManyToOne; 040import javax.persistence.NamedQueries; 041import javax.persistence.NamedQuery; 042import javax.persistence.OneToOne; 043import javax.persistence.Table; 044import javax.persistence.Temporal; 045import javax.persistence.Transient; 046 047import org.springframework.security.oauth2.common.OAuth2AccessToken; 048import org.springframework.security.oauth2.common.OAuth2RefreshToken; 049 050import com.nimbusds.jwt.JWT; 051import com.nimbusds.jwt.JWTParser; 052 053/** 054 * @author jricher 055 * 056 */ 057@Entity 058@Table(name = "access_token") 059@NamedQueries({ 060 @NamedQuery(name = "OAuth2AccessTokenEntity.getAll", query = "select a from OAuth2AccessTokenEntity a"), 061 @NamedQuery(name = "OAuth2AccessTokenEntity.getAllExpiredByDate", query = "select a from OAuth2AccessTokenEntity a where a.expiration <= :date"), 062 @NamedQuery(name = "OAuth2AccessTokenEntity.getByRefreshToken", query = "select a from OAuth2AccessTokenEntity a where a.refreshToken = :refreshToken"), 063 @NamedQuery(name = "OAuth2AccessTokenEntity.getByClient", query = "select a from OAuth2AccessTokenEntity a where a.client = :client"), 064 @NamedQuery(name = "OAuth2AccessTokenEntity.getByAuthentication", query = "select a from OAuth2AccessTokenEntity a where a.authenticationHolder.authentication = :authentication"), 065 @NamedQuery(name = "OAuth2AccessTokenEntity.getByIdToken", query = "select a from OAuth2AccessTokenEntity a where a.idToken = :idToken"), 066 @NamedQuery(name = "OAuth2AccessTokenEntity.getByTokenValue", query = "select a from OAuth2AccessTokenEntity a where a.value = :tokenValue") 067}) 068//@JsonSerialize(using = OAuth2AccessTokenSerializer.class) 069//@JsonDeserialize(using = OAuth2AccessTokenDeserializer.class) 070public class OAuth2AccessTokenEntity implements OAuth2AccessToken { 071 072 public static String ID_TOKEN_FIELD_NAME = "id_token"; 073 074 private Long id; 075 076 private ClientDetailsEntity client; 077 078 private AuthenticationHolderEntity authenticationHolder; // the authentication that made this access 079 080 private JWT jwtValue; // JWT-encoded access token value 081 082 private OAuth2AccessTokenEntity idToken; // JWT-encoded OpenID Connect IdToken 083 084 private Date expiration; 085 086 private String tokenType = OAuth2AccessToken.BEARER_TYPE; 087 088 private OAuth2RefreshTokenEntity refreshToken; 089 090 private Set<String> scope; 091 092 /** 093 * Create a new, blank access token 094 */ 095 public OAuth2AccessTokenEntity() { 096 097 } 098 099 /** 100 * @return the id 101 */ 102 @Id 103 @GeneratedValue(strategy = GenerationType.IDENTITY) 104 @Column(name = "id") 105 public Long getId() { 106 return id; 107 } 108 109 /** 110 * @param id the id to set 111 */ 112 public void setId(Long id) { 113 this.id = id; 114 } 115 116 /** 117 * Get all additional information to be sent to the serializer. Inserts a copy of the IdToken (in JWT String form). 118 */ 119 @Override 120 @Transient 121 public Map<String, Object> getAdditionalInformation() { 122 Map<String, Object> map = new HashMap<String, Object>(); //super.getAdditionalInformation(); 123 if (getIdToken() != null) { 124 map.put(ID_TOKEN_FIELD_NAME, getIdTokenString()); 125 } 126 return map; 127 } 128 129 /** 130 * The authentication in place when this token was created. 131 * @return the authentication 132 */ 133 @ManyToOne 134 @JoinColumn(name = "auth_holder_id") 135 public AuthenticationHolderEntity getAuthenticationHolder() { 136 return authenticationHolder; 137 } 138 139 /** 140 * @param authentication the authentication to set 141 */ 142 public void setAuthenticationHolder(AuthenticationHolderEntity authenticationHolder) { 143 this.authenticationHolder = authenticationHolder; 144 } 145 146 /** 147 * @return the client 148 */ 149 @ManyToOne 150 @JoinColumn(name = "client_id") 151 public ClientDetailsEntity getClient() { 152 return client; 153 } 154 155 /** 156 * @param client the client to set 157 */ 158 public void setClient(ClientDetailsEntity client) { 159 this.client = client; 160 } 161 162 /** 163 * Get the string-encoded value of this access token. 164 */ 165 @Override 166 @Basic 167 @Column(name="token_value") 168 public String getValue() { 169 return jwtValue.serialize(); 170 } 171 172 /** 173 * Set the "value" of this Access Token 174 * 175 * @param value the JWT string 176 * @throws ParseException if "value" is not a properly formatted JWT string 177 */ 178 public void setValue(String value) throws ParseException { 179 setJwt(JWTParser.parse(value)); 180 } 181 182 @Override 183 @Basic 184 @Temporal(javax.persistence.TemporalType.TIMESTAMP) 185 @Column(name = "expiration") 186 public Date getExpiration() { 187 return expiration; 188 } 189 190 public void setExpiration(Date expiration) { 191 this.expiration = expiration; 192 } 193 194 @Override 195 @Basic 196 @Column(name="token_type") 197 public String getTokenType() { 198 return tokenType; 199 } 200 201 public void setTokenType(String tokenType) { 202 this.tokenType = tokenType; 203 } 204 205 @Override 206 @ManyToOne 207 @JoinColumn(name="refresh_token_id") 208 public OAuth2RefreshTokenEntity getRefreshToken() { 209 return refreshToken; 210 } 211 212 public void setRefreshToken(OAuth2RefreshTokenEntity refreshToken) { 213 this.refreshToken = refreshToken; 214 } 215 216 public void setRefreshToken(OAuth2RefreshToken refreshToken) { 217 if (!(refreshToken instanceof OAuth2RefreshTokenEntity)) { 218 throw new IllegalArgumentException("Not a storable refresh token entity!"); 219 } 220 // force a pass through to the entity version 221 setRefreshToken((OAuth2RefreshTokenEntity)refreshToken); 222 } 223 224 @Override 225 @ElementCollection(fetch=FetchType.EAGER) 226 @CollectionTable( 227 joinColumns=@JoinColumn(name="owner_id"), 228 name="token_scope" 229 ) 230 public Set<String> getScope() { 231 return scope; 232 } 233 234 public void setScope(Set<String> scope) { 235 this.scope = scope; 236 } 237 238 @Override 239 @Transient 240 public boolean isExpired() { 241 return getExpiration() == null ? false : System.currentTimeMillis() > getExpiration().getTime(); 242 } 243 244 /** 245 * @return the idToken 246 */ 247 @OneToOne(cascade=CascadeType.ALL) // one-to-one mapping for now 248 @JoinColumn(name = "id_token_id") 249 public OAuth2AccessTokenEntity getIdToken() { 250 return idToken; 251 } 252 253 /** 254 * @param idToken the idToken to set 255 */ 256 public void setIdToken(OAuth2AccessTokenEntity idToken) { 257 this.idToken = idToken; 258 } 259 260 /** 261 * @return the idTokenString 262 */ 263 @Transient 264 public String getIdTokenString() { 265 if (idToken != null) { 266 return idToken.getValue(); // get the JWT string value of the id token entity 267 } else { 268 return null; 269 } 270 } 271 272 /** 273 * @return the jwtValue 274 */ 275 @Transient 276 public JWT getJwt() { 277 return jwtValue; 278 } 279 280 /** 281 * @param jwtValue the jwtValue to set 282 */ 283 public void setJwt(JWT jwt) { 284 this.jwtValue = jwt; 285 } 286 287 @Override 288 @Transient 289 public int getExpiresIn() { 290 291 if (getExpiration() == null) { 292 return -1; // no expiration time 293 } else { 294 int secondsRemaining = (int) ((getExpiration().getTime() - System.currentTimeMillis()) / 1000); 295 if (isExpired()) { 296 return 0; // has an expiration time and expired 297 } else { // has an expiration time and not expired 298 return secondsRemaining; 299 } 300 } 301 } 302 303}