keycloak-uncached

Details

diff --git a/saml-core/src/main/java/org/keycloak/rotation/CompositeKeyLocator.java b/saml-core/src/main/java/org/keycloak/rotation/CompositeKeyLocator.java
new file mode 100644
index 0000000..4b3cb57
--- /dev/null
+++ b/saml-core/src/main/java/org/keycloak/rotation/CompositeKeyLocator.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.rotation;
+
+import java.security.Key;
+import java.security.KeyManagementException;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * {@link KeyLocator} that represents a list of multiple {@link KeyLocator}s. Key is searched
+ * from the first to the last {@link KeyLocator} in the order given by the list. If there are
+ * multiple {@link KeyLocator}s providing key with the same key ID, the first matching key is
+ * returned.
+ *
+ * @author hmlnarik
+ */
+public class CompositeKeyLocator implements KeyLocator, Iterable<Key> {
+
+    private final List<KeyLocator> keyLocators = new LinkedList<>();
+
+    @Override
+    public Key getKey(String kid) throws KeyManagementException {
+        for (KeyLocator keyLocator : keyLocators) {
+            Key k = keyLocator.getKey(kid);
+            if (k != null) {
+                return k;
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public void refreshKeyCache() {
+        for (KeyLocator keyLocator : keyLocators) {
+            keyLocator.refreshKeyCache();
+        }
+    }
+
+    /**
+     * Registers a given {@link KeyLocator} as the first {@link KeyLocator}.
+     */
+    public void addFirst(KeyLocator keyLocator) {
+        this.keyLocators.add(0, keyLocator);
+    }
+
+    /**
+     * Registers a given {@link KeyLocator} as the last {@link KeyLocator}.
+     */
+    public void add(KeyLocator keyLocator) {
+        this.keyLocators.add(keyLocator);
+    }
+
+    /**
+     * Clears the list of registered {@link KeyLocator}s
+     */
+    public void clear() {
+        this.keyLocators.clear();
+    }
+
+    @Override
+    public String toString() {
+        if (this.keyLocators.size() == 1) {
+            return this.keyLocators.get(0).toString();
+        }
+
+        StringBuilder sb = new StringBuilder("Key locator chain: [");
+        for (Iterator<KeyLocator> it = keyLocators.iterator(); it.hasNext();) {
+            KeyLocator keyLocator = it.next();
+            sb.append(keyLocator.toString());
+            if (it.hasNext()) {
+                sb.append(", ");
+            }
+        }
+        return sb.append("]").toString();
+    }
+
+    @Override
+    public Iterator<Key> iterator() {
+        final Iterator<Iterable<Key>> iterablesIterator = getKeyLocatorIterators().iterator();
+
+        return new JointKeyIterator(iterablesIterator).iterator();
+    }
+
+    @SuppressWarnings("unchecked")
+    private Iterable<Iterable<Key>> getKeyLocatorIterators() {
+        List<Iterable<Key>> res = new LinkedList<>();
+        for (KeyLocator kl : this.keyLocators) {
+            if (kl instanceof Iterable) {
+                res.add(((Iterable<Key>) kl));
+            }
+        }
+        return Collections.unmodifiableCollection(res);
+    }
+
+    private class JointKeyIterator implements Iterable<Key> {
+
+        // based on http://stackoverflow.com/a/34126154/6930869
+        private final Iterator<Iterable<Key>> iterablesIterator;
+
+        public JointKeyIterator(Iterator<Iterable<Key>> iterablesIterator) {
+            this.iterablesIterator = iterablesIterator;
+        }
+
+        @Override
+        public Iterator<Key> iterator() {
+            if (! iterablesIterator.hasNext()) {
+                return Collections.<Key>emptyIterator();
+            }
+
+            return new Iterator<Key>() {
+                private Iterator<Key> currentIterator = nextIterator();
+
+                @Override
+                public boolean hasNext() {
+                    return currentIterator.hasNext();
+                }
+
+                @Override
+                public Key next() {
+                    final Key next = currentIterator.next();
+                    findNext();
+                    return next;
+                }
+
+                private Iterator<Key> nextIterator() {
+                    return iterablesIterator.next().iterator();
+                }
+
+                private Iterator<Key> findNext() {
+                    while (! currentIterator.hasNext()) {
+                        if (! iterablesIterator.hasNext()) {
+                            break;
+                        }
+                        currentIterator = nextIterator();
+                    }
+                    return this;
+                }
+            }.findNext();
+        }
+    }
+}
diff --git a/saml-core/src/main/java/org/keycloak/rotation/HardcodedKeyLocator.java b/saml-core/src/main/java/org/keycloak/rotation/HardcodedKeyLocator.java
new file mode 100644
index 0000000..ae2615a
--- /dev/null
+++ b/saml-core/src/main/java/org/keycloak/rotation/HardcodedKeyLocator.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.rotation;
+
+import java.security.Key;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+/**
+ * Key locator that always returns a specified key.
+ *
+ * @author <a href="mailto:hmlnarik@redhat.com">Hynek Mlnařík</a>
+ */
+public class HardcodedKeyLocator implements KeyLocator, Iterable<Key> {
+
+    private final Collection<? extends Key> keys;
+
+    public HardcodedKeyLocator(Key key) {
+        this.keys = Collections.singleton(key);
+    }
+
+    public HardcodedKeyLocator(Collection<? extends Key> keys) {
+        if (keys == null) {
+            throw new NullPointerException("keys");
+        }
+        this.keys = new LinkedList<>(keys);
+    }
+
+    @Override
+    public Key getKey(String kid) {
+        if (this.keys.size() == 1) {
+            return this.keys.iterator().next();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void refreshKeyCache() {
+        // do nothing
+    }
+
+    @Override
+    public String toString() {
+        return "hardcoded keys, count: " + this.keys.size();
+    }
+
+    @Override
+    public Iterator<Key> iterator() {
+        return Collections.unmodifiableCollection(keys).iterator();
+    }
+}
diff --git a/saml-core/src/main/java/org/keycloak/rotation/KeyLocator.java b/saml-core/src/main/java/org/keycloak/rotation/KeyLocator.java
new file mode 100644
index 0000000..7112eca
--- /dev/null
+++ b/saml-core/src/main/java/org/keycloak/rotation/KeyLocator.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.rotation;
+
+import java.security.Key;
+import java.security.KeyManagementException;
+
+/**
+ * This interface defines a method for obtaining a security key by ID.
+ * <p>
+ * If the {@code KeyLocator} implementor wants to make all its keys available for iteration,
+ * it should implement {@link Iterable}&lt;{@code T extends }{@link Key}&gt; interface.
+ * The base {@code KeyLocator} does not extend this interface to enable {@code KeyLocators}
+ * that do not support listing their keys.
+ *
+ * @author <a href="mailto:hmlnarik@redhat.com">Hynek Mlnařík</a>
+ */
+public interface KeyLocator {
+
+    /**
+     * Returns a key with a particular ID.
+     * @param kid Key ID
+     * @param configuration Configuration
+     * @return key, which should be used for verify signature on given "input"
+     * @throws KeyManagementException
+     */
+    Key getKey(String kid) throws KeyManagementException;
+
+    /**
+     * If this key locator caches keys in any way, forces this cache cleanup
+     * and refreshing the keys.
+     */
+    void refreshKeyCache();
+
+}