001/******************************************************************************* 002 * Copyright 2018 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 @NamedQuery(name = OAuth2AccessTokenEntity.QUERY_BY_NAME, query = "select r from OAuth2AccessTokenEntity r where r.authenticationHolder.userAuth.name = :" + OAuth2AccessTokenEntity.PARAM_NAME) 076}) 077@org.codehaus.jackson.map.annotate.JsonSerialize(using = OAuth2AccessTokenJackson1Serializer.class) 078@org.codehaus.jackson.map.annotate.JsonDeserialize(using = OAuth2AccessTokenJackson1Deserializer.class) 079@com.fasterxml.jackson.databind.annotation.JsonSerialize(using = OAuth2AccessTokenJackson2Serializer.class) 080@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = OAuth2AccessTokenJackson2Deserializer.class) 081public class OAuth2AccessTokenEntity implements OAuth2AccessToken { 082 083 public static final String QUERY_BY_APPROVED_SITE = "OAuth2AccessTokenEntity.getByApprovedSite"; 084 public static final String QUERY_BY_TOKEN_VALUE = "OAuth2AccessTokenEntity.getByTokenValue"; 085 public static final String QUERY_BY_CLIENT = "OAuth2AccessTokenEntity.getByClient"; 086 public static final String QUERY_BY_REFRESH_TOKEN = "OAuth2AccessTokenEntity.getByRefreshToken"; 087 public static final String QUERY_EXPIRED_BY_DATE = "OAuth2AccessTokenEntity.getAllExpiredByDate"; 088 public static final String QUERY_ALL = "OAuth2AccessTokenEntity.getAll"; 089 public static final String QUERY_BY_RESOURCE_SET = "OAuth2AccessTokenEntity.getByResourceSet"; 090 public static final String QUERY_BY_NAME = "OAuth2AccessTokenEntity.getByName"; 091 092 public static final String PARAM_TOKEN_VALUE = "tokenValue"; 093 public static final String PARAM_CLIENT = "client"; 094 public static final String PARAM_REFERSH_TOKEN = "refreshToken"; 095 public static final String PARAM_DATE = "date"; 096 public static final String PARAM_RESOURCE_SET_ID = "rsid"; 097 public static final String PARAM_APPROVED_SITE = "approvedSite"; 098 public static final String PARAM_NAME = "name"; 099 100 public static final String ID_TOKEN_FIELD_NAME = "id_token"; 101 102 private Long id; 103 104 private ClientDetailsEntity client; 105 106 private AuthenticationHolderEntity authenticationHolder; // the authentication that made this access 107 108 private JWT jwtValue; // JWT-encoded access token value 109 110 private Date expiration; 111 112 private String tokenType = OAuth2AccessToken.BEARER_TYPE; 113 114 private OAuth2RefreshTokenEntity refreshToken; 115 116 private Set<String> scope; 117 118 private Set<Permission> permissions; 119 120 private ApprovedSite approvedSite; 121 122 private Map<String, Object> additionalInformation = new HashMap<>(); // ephemeral map of items to be added to the OAuth token response 123 124 /** 125 * Create a new, blank access token 126 */ 127 public OAuth2AccessTokenEntity() { 128 129 } 130 131 /** 132 * @return the id 133 */ 134 @Id 135 @GeneratedValue(strategy = GenerationType.IDENTITY) 136 @Column(name = "id") 137 public Long getId() { 138 return id; 139 } 140 141 /** 142 * @param id the id to set 143 */ 144 public void setId(Long id) { 145 this.id = id; 146 } 147 148 /** 149 * Get all additional information to be sent to the serializer as part of the token response. 150 * This map is not persisted to the database. 151 */ 152 @Override 153 @Transient 154 public Map<String, Object> getAdditionalInformation() { 155 return additionalInformation; 156 } 157 158 /** 159 * The authentication in place when this token was created. 160 * @return the authentication 161 */ 162 @ManyToOne 163 @JoinColumn(name = "auth_holder_id") 164 public AuthenticationHolderEntity getAuthenticationHolder() { 165 return authenticationHolder; 166 } 167 168 /** 169 * @param authentication the authentication to set 170 */ 171 public void setAuthenticationHolder(AuthenticationHolderEntity authenticationHolder) { 172 this.authenticationHolder = authenticationHolder; 173 } 174 175 /** 176 * @return the client 177 */ 178 @ManyToOne 179 @JoinColumn(name = "client_id") 180 public ClientDetailsEntity getClient() { 181 return client; 182 } 183 184 /** 185 * @param client the client to set 186 */ 187 public void setClient(ClientDetailsEntity client) { 188 this.client = client; 189 } 190 191 /** 192 * Get the string-encoded value of this access token. 193 */ 194 @Override 195 @Transient 196 public String getValue() { 197 return jwtValue.serialize(); 198 } 199 200 @Override 201 @Basic 202 @Temporal(javax.persistence.TemporalType.TIMESTAMP) 203 @Column(name = "expiration") 204 public Date getExpiration() { 205 return expiration; 206 } 207 208 public void setExpiration(Date expiration) { 209 this.expiration = expiration; 210 } 211 212 @Override 213 @Basic 214 @Column(name="token_type") 215 public String getTokenType() { 216 return tokenType; 217 } 218 219 public void setTokenType(String tokenType) { 220 this.tokenType = tokenType; 221 } 222 223 @Override 224 @ManyToOne 225 @JoinColumn(name="refresh_token_id") 226 public OAuth2RefreshTokenEntity getRefreshToken() { 227 return refreshToken; 228 } 229 230 public void setRefreshToken(OAuth2RefreshTokenEntity refreshToken) { 231 this.refreshToken = refreshToken; 232 } 233 234 public void setRefreshToken(OAuth2RefreshToken refreshToken) { 235 if (!(refreshToken instanceof OAuth2RefreshTokenEntity)) { 236 throw new IllegalArgumentException("Not a storable refresh token entity!"); 237 } 238 // force a pass through to the entity version 239 setRefreshToken((OAuth2RefreshTokenEntity)refreshToken); 240 } 241 242 @Override 243 @ElementCollection(fetch=FetchType.EAGER) 244 @CollectionTable( 245 joinColumns=@JoinColumn(name="owner_id"), 246 name="token_scope" 247 ) 248 public Set<String> getScope() { 249 return scope; 250 } 251 252 public void setScope(Set<String> scope) { 253 this.scope = scope; 254 } 255 256 @Override 257 @Transient 258 public boolean isExpired() { 259 return getExpiration() == null ? false : System.currentTimeMillis() > getExpiration().getTime(); 260 } 261 262 /** 263 * @return the jwtValue 264 */ 265 @Basic 266 @Column(name="token_value") 267 @Convert(converter = JWTStringConverter.class) 268 public JWT getJwt() { 269 return jwtValue; 270 } 271 272 /** 273 * @param jwtValue the jwtValue to set 274 */ 275 public void setJwt(JWT jwt) { 276 this.jwtValue = jwt; 277 } 278 279 @Override 280 @Transient 281 public int getExpiresIn() { 282 283 if (getExpiration() == null) { 284 return -1; // no expiration time 285 } else { 286 int secondsRemaining = (int) ((getExpiration().getTime() - System.currentTimeMillis()) / 1000); 287 if (isExpired()) { 288 return 0; // has an expiration time and expired 289 } else { // has an expiration time and not expired 290 return secondsRemaining; 291 } 292 } 293 } 294 295 /** 296 * @return the permissions 297 */ 298 @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) 299 @JoinTable( 300 name = "access_token_permissions", 301 joinColumns = @JoinColumn(name = "access_token_id"), 302 inverseJoinColumns = @JoinColumn(name = "permission_id") 303 ) 304 public Set<Permission> getPermissions() { 305 return permissions; 306 } 307 308 /** 309 * @param permissions the permissions to set 310 */ 311 public void setPermissions(Set<Permission> permissions) { 312 this.permissions = permissions; 313 } 314 315 @ManyToOne 316 @JoinColumn(name="approved_site_id") 317 public ApprovedSite getApprovedSite() { 318 return approvedSite; 319 } 320 321 public void setApprovedSite(ApprovedSite approvedSite) { 322 this.approvedSite = approvedSite; 323 } 324 325 /** 326 * Add the ID Token to the additionalInformation map for a token response. 327 * @param idToken 328 */ 329 @Transient 330 public void setIdToken(JWT idToken) { 331 if (idToken != null) { 332 additionalInformation.put(ID_TOKEN_FIELD_NAME, idToken.serialize()); 333 } 334 } 335}