001/******************************************************************************* 002 * Copyright 2017 The MIT Internet Trust Consortium 003 * 004 * Portions copyright 2011-2013 The MITRE Corporation 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 *******************************************************************************/ 018/** 019 * 020 */ 021package org.mitre.oauth2.model; 022 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.Convert; 033import javax.persistence.ElementCollection; 034import javax.persistence.Entity; 035import javax.persistence.FetchType; 036import javax.persistence.GeneratedValue; 037import javax.persistence.GenerationType; 038import javax.persistence.Id; 039import javax.persistence.JoinColumn; 040import javax.persistence.JoinTable; 041import javax.persistence.ManyToOne; 042import javax.persistence.NamedQueries; 043import javax.persistence.NamedQuery; 044import javax.persistence.OneToMany; 045import javax.persistence.Table; 046import javax.persistence.Temporal; 047import javax.persistence.Transient; 048 049import org.mitre.oauth2.model.convert.JWTStringConverter; 050import org.mitre.openid.connect.model.ApprovedSite; 051import org.mitre.uma.model.Permission; 052import org.springframework.security.oauth2.common.OAuth2AccessToken; 053import org.springframework.security.oauth2.common.OAuth2AccessTokenJackson1Deserializer; 054import org.springframework.security.oauth2.common.OAuth2AccessTokenJackson1Serializer; 055import org.springframework.security.oauth2.common.OAuth2AccessTokenJackson2Deserializer; 056import org.springframework.security.oauth2.common.OAuth2AccessTokenJackson2Serializer; 057import org.springframework.security.oauth2.common.OAuth2RefreshToken; 058 059import com.nimbusds.jwt.JWT; 060 061/** 062 * @author jricher 063 * 064 */ 065@Entity 066@Table(name = "access_token") 067@NamedQueries({ 068 @NamedQuery(name = OAuth2AccessTokenEntity.QUERY_ALL, query = "select a from OAuth2AccessTokenEntity a"), 069 @NamedQuery(name = OAuth2AccessTokenEntity.QUERY_EXPIRED_BY_DATE, query = "select a from OAuth2AccessTokenEntity a where a.expiration <= :" + OAuth2AccessTokenEntity.PARAM_DATE), 070 @NamedQuery(name = OAuth2AccessTokenEntity.QUERY_BY_REFRESH_TOKEN, query = "select a from OAuth2AccessTokenEntity a where a.refreshToken = :" + OAuth2AccessTokenEntity.PARAM_REFERSH_TOKEN), 071 @NamedQuery(name = OAuth2AccessTokenEntity.QUERY_BY_CLIENT, query = "select a from OAuth2AccessTokenEntity a where a.client = :" + OAuth2AccessTokenEntity.PARAM_CLIENT), 072 @NamedQuery(name = OAuth2AccessTokenEntity.QUERY_BY_TOKEN_VALUE, query = "select a from OAuth2AccessTokenEntity a where a.jwt = :" + OAuth2AccessTokenEntity.PARAM_TOKEN_VALUE), 073 @NamedQuery(name = OAuth2AccessTokenEntity.QUERY_BY_APPROVED_SITE, query = "select a from OAuth2AccessTokenEntity a where a.approvedSite = :" + OAuth2AccessTokenEntity.PARAM_APPROVED_SITE), 074 @NamedQuery(name = OAuth2AccessTokenEntity.QUERY_BY_RESOURCE_SET, query = "select a from OAuth2AccessTokenEntity a join a.permissions p where p.resourceSet.id = :" + OAuth2AccessTokenEntity.PARAM_RESOURCE_SET_ID) 075}) 076@org.codehaus.jackson.map.annotate.JsonSerialize(using = OAuth2AccessTokenJackson1Serializer.class) 077@org.codehaus.jackson.map.annotate.JsonDeserialize(using = OAuth2AccessTokenJackson1Deserializer.class) 078@com.fasterxml.jackson.databind.annotation.JsonSerialize(using = OAuth2AccessTokenJackson2Serializer.class) 079@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = OAuth2AccessTokenJackson2Deserializer.class) 080public class OAuth2AccessTokenEntity implements OAuth2AccessToken { 081 082 public static final String QUERY_BY_APPROVED_SITE = "OAuth2AccessTokenEntity.getByApprovedSite"; 083 public static final String QUERY_BY_TOKEN_VALUE = "OAuth2AccessTokenEntity.getByTokenValue"; 084 public static final String QUERY_BY_CLIENT = "OAuth2AccessTokenEntity.getByClient"; 085 public static final String QUERY_BY_REFRESH_TOKEN = "OAuth2AccessTokenEntity.getByRefreshToken"; 086 public static final String QUERY_EXPIRED_BY_DATE = "OAuth2AccessTokenEntity.getAllExpiredByDate"; 087 public static final String QUERY_ALL = "OAuth2AccessTokenEntity.getAll"; 088 public static final String QUERY_BY_RESOURCE_SET = "OAuth2AccessTokenEntity.getByResourceSet"; 089 090 public static final String PARAM_TOKEN_VALUE = "tokenValue"; 091 public static final String PARAM_CLIENT = "client"; 092 public static final String PARAM_REFERSH_TOKEN = "refreshToken"; 093 public static final String PARAM_DATE = "date"; 094 public static final String PARAM_RESOURCE_SET_ID = "rsid"; 095 public static final String PARAM_APPROVED_SITE = "approvedSite"; 096 097 public static final String ID_TOKEN_FIELD_NAME = "id_token"; 098 099 private Long id; 100 101 private ClientDetailsEntity client; 102 103 private AuthenticationHolderEntity authenticationHolder; // the authentication that made this access 104 105 private JWT jwtValue; // JWT-encoded access token value 106 107 private Date expiration; 108 109 private String tokenType = OAuth2AccessToken.BEARER_TYPE; 110 111 private OAuth2RefreshTokenEntity refreshToken; 112 113 private Set<String> scope; 114 115 private Set<Permission> permissions; 116 117 private ApprovedSite approvedSite; 118 119 private Map<String, Object> additionalInformation = new HashMap<>(); // ephemeral map of items to be added to the OAuth token response 120 121 /** 122 * Create a new, blank access token 123 */ 124 public OAuth2AccessTokenEntity() { 125 126 } 127 128 /** 129 * @return the id 130 */ 131 @Id 132 @GeneratedValue(strategy = GenerationType.IDENTITY) 133 @Column(name = "id") 134 public Long getId() { 135 return id; 136 } 137 138 /** 139 * @param id the id to set 140 */ 141 public void setId(Long id) { 142 this.id = id; 143 } 144 145 /** 146 * Get all additional information to be sent to the serializer as part of the token response. 147 * This map is not persisted to the database. 148 */ 149 @Override 150 @Transient 151 public Map<String, Object> getAdditionalInformation() { 152 return additionalInformation; 153 } 154 155 /** 156 * The authentication in place when this token was created. 157 * @return the authentication 158 */ 159 @ManyToOne 160 @JoinColumn(name = "auth_holder_id") 161 public AuthenticationHolderEntity getAuthenticationHolder() { 162 return authenticationHolder; 163 } 164 165 /** 166 * @param authentication the authentication to set 167 */ 168 public void setAuthenticationHolder(AuthenticationHolderEntity authenticationHolder) { 169 this.authenticationHolder = authenticationHolder; 170 } 171 172 /** 173 * @return the client 174 */ 175 @ManyToOne 176 @JoinColumn(name = "client_id") 177 public ClientDetailsEntity getClient() { 178 return client; 179 } 180 181 /** 182 * @param client the client to set 183 */ 184 public void setClient(ClientDetailsEntity client) { 185 this.client = client; 186 } 187 188 /** 189 * Get the string-encoded value of this access token. 190 */ 191 @Override 192 @Transient 193 public String getValue() { 194 return jwtValue.serialize(); 195 } 196 197 @Override 198 @Basic 199 @Temporal(javax.persistence.TemporalType.TIMESTAMP) 200 @Column(name = "expiration") 201 public Date getExpiration() { 202 return expiration; 203 } 204 205 public void setExpiration(Date expiration) { 206 this.expiration = expiration; 207 } 208 209 @Override 210 @Basic 211 @Column(name="token_type") 212 public String getTokenType() { 213 return tokenType; 214 } 215 216 public void setTokenType(String tokenType) { 217 this.tokenType = tokenType; 218 } 219 220 @Override 221 @ManyToOne 222 @JoinColumn(name="refresh_token_id") 223 public OAuth2RefreshTokenEntity getRefreshToken() { 224 return refreshToken; 225 } 226 227 public void setRefreshToken(OAuth2RefreshTokenEntity refreshToken) { 228 this.refreshToken = refreshToken; 229 } 230 231 public void setRefreshToken(OAuth2RefreshToken refreshToken) { 232 if (!(refreshToken instanceof OAuth2RefreshTokenEntity)) { 233 throw new IllegalArgumentException("Not a storable refresh token entity!"); 234 } 235 // force a pass through to the entity version 236 setRefreshToken((OAuth2RefreshTokenEntity)refreshToken); 237 } 238 239 @Override 240 @ElementCollection(fetch=FetchType.EAGER) 241 @CollectionTable( 242 joinColumns=@JoinColumn(name="owner_id"), 243 name="token_scope" 244 ) 245 public Set<String> getScope() { 246 return scope; 247 } 248 249 public void setScope(Set<String> scope) { 250 this.scope = scope; 251 } 252 253 @Override 254 @Transient 255 public boolean isExpired() { 256 return getExpiration() == null ? false : System.currentTimeMillis() > getExpiration().getTime(); 257 } 258 259 /** 260 * @return the jwtValue 261 */ 262 @Basic 263 @Column(name="token_value") 264 @Convert(converter = JWTStringConverter.class) 265 public JWT getJwt() { 266 return jwtValue; 267 } 268 269 /** 270 * @param jwtValue the jwtValue to set 271 */ 272 public void setJwt(JWT jwt) { 273 this.jwtValue = jwt; 274 } 275 276 @Override 277 @Transient 278 public int getExpiresIn() { 279 280 if (getExpiration() == null) { 281 return -1; // no expiration time 282 } else { 283 int secondsRemaining = (int) ((getExpiration().getTime() - System.currentTimeMillis()) / 1000); 284 if (isExpired()) { 285 return 0; // has an expiration time and expired 286 } else { // has an expiration time and not expired 287 return secondsRemaining; 288 } 289 } 290 } 291 292 /** 293 * @return the permissions 294 */ 295 @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) 296 @JoinTable( 297 name = "access_token_permissions", 298 joinColumns = @JoinColumn(name = "access_token_id"), 299 inverseJoinColumns = @JoinColumn(name = "permission_id") 300 ) 301 public Set<Permission> getPermissions() { 302 return permissions; 303 } 304 305 /** 306 * @param permissions the permissions to set 307 */ 308 public void setPermissions(Set<Permission> permissions) { 309 this.permissions = permissions; 310 } 311 312 @ManyToOne 313 @JoinColumn(name="approved_site_id") 314 public ApprovedSite getApprovedSite() { 315 return approvedSite; 316 } 317 318 public void setApprovedSite(ApprovedSite approvedSite) { 319 this.approvedSite = approvedSite; 320 } 321 322 /** 323 * Add the ID Token to the additionalInformation map for a token response. 324 * @param idToken 325 */ 326 @Transient 327 public void setIdToken(JWT idToken) { 328 if (idToken != null) { 329 additionalInformation.put(ID_TOKEN_FIELD_NAME, idToken.serialize()); 330 } 331 } 332}