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}