1 /**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 package org.apache.ws.security.cache;
21
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.Date;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map.Entry;
29 import java.util.Set;
30 import java.util.SortedMap;
31 import java.util.TreeMap;
32
33 /**
34 * A simple in-memory HashSet based cache to prevent against replay attacks. The default TTL is 5 minutes
35 * and the max TTL is 60 minutes.
36 */
37 public class MemoryReplayCache implements ReplayCache {
38
39 public static final long DEFAULT_TTL = 60L * 5L;
40 public static final long MAX_TTL = DEFAULT_TTL * 12L;
41 private SortedMap<Date, List<String>> cache = new TreeMap<Date, List<String>>();
42 private Set<String> ids = Collections.synchronizedSet(new HashSet<String>());
43
44 /**
45 * Add the given identifier to the cache. It will be cached for a default amount of time.
46 * @param identifier The identifier to be added
47 */
48 public void add(String identifier) {
49 add(identifier, DEFAULT_TTL);
50 }
51
52 /**
53 * Add the given identifier to the cache to be cached for the given time
54 * @param identifier The identifier to be added
55 * @param timeToLive The length of time to cache the Identifier in seconds
56 */
57 public void add(String identifier, long timeToLive) {
58 if (identifier == null || "".equals(identifier)) {
59 return;
60 }
61
62 long ttl = timeToLive;
63 if (ttl < 0 || ttl > MAX_TTL) {
64 ttl = DEFAULT_TTL;
65 }
66
67 Date expires = new Date();
68 long currentTime = expires.getTime();
69 expires.setTime(currentTime + (ttl * 1000L));
70
71 synchronized (cache) {
72 List<String> list = cache.get(expires);
73 if (list == null) {
74 list = new ArrayList<String>(1);
75 cache.put(expires, list);
76 }
77 list.add(identifier);
78 }
79 ids.add(identifier);
80 }
81
82 /**
83 * Return true if the given identifier is contained in the cache
84 * @param identifier The identifier to check
85 */
86 public boolean contains(String identifier) {
87 processTokenExpiry();
88
89 if (identifier != null && !"".equals(identifier)) {
90 return ids.contains(identifier);
91 }
92 return false;
93 }
94
95 protected void processTokenExpiry() {
96 Date current = new Date();
97 synchronized (cache) {
98 Iterator<Entry<Date, List<String>>> it = cache.entrySet().iterator();
99 while (it.hasNext()) {
100 Entry<Date, List<String>> entry = it.next();
101 if (entry.getKey().before(current)) {
102 for (String id : entry.getValue()) {
103 ids.remove(id);
104 }
105 it.remove();
106 } else {
107 break;
108 }
109 }
110 }
111 }
112 }