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}