001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2016, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * 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 distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.jose.jwk.source;
019
020
021import java.util.Date;
022import java.util.concurrent.TimeUnit;
023
024import net.jcip.annotations.ThreadSafe;
025
026import com.nimbusds.jose.jwk.JWKSet;
027
028
029/**
030 * JSON Web Key (JWK) set cache implementation.
031 *
032 * @author Vladimir Dzhuvinov
033 * @author Sarvesh Sharma
034 * @version 2020-12-27
035 */
036@ThreadSafe
037public class DefaultJWKSetCache implements JWKSetCache {
038        
039        
040        /**
041         * The default lifespan for cached JWK sets (15 minutes).
042         */
043        public static final long DEFAULT_LIFESPAN_MINUTES = 15;
044
045
046        /**
047         * The default refresh time for cached JWK sets (5 minutes).
048         */
049        public static final long DEFAULT_REFRESH_TIME_MINUTES = 5;
050
051        
052        /**
053         * The lifespan of the cached JWK set, in {@link #timeUnit}s, negative
054         * means no expiration.
055         */
056        private final long lifespan;
057
058
059        /**
060         * The refresh time of the cached JWK set, in {@link #timeUnit}s,
061         * negative means no refresh time.
062         */
063        private final long refreshTime;
064
065        
066        /**
067         * The time unit, may be {@code null} if no expiration / refresh time.
068         */
069        private final TimeUnit timeUnit;
070        
071        
072        /**
073         * The cached JWK set, {@code null} if none.
074         */
075        private volatile JWKSetWithTimestamp jwkSetWithTimestamp;
076        
077        
078        /**
079         * Creates a new JWK set, the default lifespan of the cached JWK set is
080         * set to 15 minutes, the refresh time to 5 minutes.
081         */
082        public DefaultJWKSetCache() {
083                
084                this(DEFAULT_LIFESPAN_MINUTES, DEFAULT_REFRESH_TIME_MINUTES, TimeUnit.MINUTES);
085        }
086        
087        
088        /**
089         * Creates a new JWK set cache.
090         *
091         * @param lifespan    The lifespan of the cached JWK set before it
092         *                    expires, negative means no expiration.
093         * @param refreshTime The time after which the cached JWK set is marked
094         *                    for refresh, negative if not specified. Should be
095         *                    shorter or equal to the lifespan.
096         * @param timeUnit    The lifespan time unit, may be {@code null} if no
097         *                    expiration or refresh time.
098         */
099        public DefaultJWKSetCache(final long lifespan, final long refreshTime, final TimeUnit timeUnit) {
100                
101                this.lifespan = lifespan;
102                this.refreshTime = refreshTime;
103
104                if ((lifespan > -1 || refreshTime > -1) && timeUnit == null) {
105                        throw new IllegalArgumentException("A time unit must be specified for non-negative lifespans or refresh times");
106                }
107                
108                this.timeUnit = timeUnit;
109        }
110        
111        
112        @Override
113        public void put(final JWKSet jwkSet) {
114                
115                if (jwkSet != null) {
116                        jwkSetWithTimestamp = new JWKSetWithTimestamp(jwkSet);
117                } else {
118                        // clear cache
119                        jwkSetWithTimestamp = null;
120                }
121        }
122        
123        
124        @Override
125        public JWKSet get() {
126                
127                if (jwkSetWithTimestamp == null || isExpired()) {
128                        return null;
129                }
130                
131                return jwkSetWithTimestamp.getJWKSet();
132        }
133
134
135        @Override
136        public boolean requiresRefresh() {
137
138                return jwkSetWithTimestamp != null &&
139                        refreshTime > -1 &&
140                        new Date().getTime() > jwkSetWithTimestamp.getDate().getTime() + TimeUnit.MILLISECONDS.convert(refreshTime, timeUnit);
141        }
142
143        
144        /**
145         * Returns the cache put timestamp.
146         *
147         * @return The cache put timestamp, negative if not specified.
148         */
149        public long getPutTimestamp() {
150                
151                return jwkSetWithTimestamp != null ? jwkSetWithTimestamp.getDate().getTime() : -1L;
152        }
153        
154        
155        /**
156         * Returns {@code true} if the cached JWK set is expired.
157         *
158         * @return {@code true} if expired.
159         */
160        public boolean isExpired() {
161        
162                return jwkSetWithTimestamp != null &&
163                        lifespan > -1 &&
164                        new Date().getTime() > jwkSetWithTimestamp.getDate().getTime() + TimeUnit.MILLISECONDS.convert(lifespan, timeUnit);
165        }
166        
167        
168        /**
169         * Returns the configured lifespan of the cached JWK.
170         *
171         * @param timeUnit The time unit to use.
172         *
173         * @return The configured lifespan, negative means no expiration.
174         */
175        public long getLifespan(final TimeUnit timeUnit) {
176                
177                if (lifespan < 0) {
178                        return lifespan;
179                }
180
181                return timeUnit.convert(lifespan, this.timeUnit);
182        }
183
184
185        /**
186         * Returns the configured refresh time of the cached JWK.
187         *
188         * @param timeUnit The time unit to use.
189         *
190         * @return The configured refresh time, negative means no expiration.
191         */
192        public long getRefreshTime(final TimeUnit timeUnit) {
193
194                if (refreshTime < 0) {
195                        return refreshTime;
196                }
197
198                return timeUnit.convert(refreshTime, this.timeUnit);
199        }
200}