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}