blob: 64f8b6ee05c597245186d43575fd322728cbc320 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.nifi.web.security.otp
import org.apache.nifi.web.security.token.OtpAuthenticationToken
import org.apache.nifi.web.security.util.CacheKey
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.junit.After
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.security.Security
import java.util.concurrent.TimeUnit
@RunWith(JUnit4.class)
class TokenCacheTest extends GroovyTestCase {
private static final Logger logger = LoggerFactory.getLogger(TokenCache.class)
private static final String andy = "alopresto"
private static final String nathan = "ngough"
private static final String matt = "mgilman"
private static final int LONG_CACHE_EXPIRATION = 10
private static final int SHORT_CACHE_EXPIRATION = 1
@BeforeClass
static void setUpOnce() throws Exception {
Security.addProvider(new BouncyCastleProvider())
logger.metaClass.methodMissing = { String name, args ->
logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
}
}
@Before
void setUp() throws Exception {
}
@After
void tearDown() throws Exception {
}
/**
* Returns a simple "hash" of the provided principal (for test purposes, simply reverses the String).
*
* @param principal the token principal
* @return the hashed token output
*/
private static String hash(def principal) {
principal.toString().reverse()
}
/**
* Returns the {@link CacheKey} constructed from the provided token.
*
* @param token the authentication token
* @return the cache key
*/
private static CacheKey buildCacheKey(OtpAuthenticationToken token) {
new CacheKey(hash(token.principal))
}
@Test
void testShouldCheckIfContainsValue() throws Exception {
// Arrange
TokenCache tokenCache = new TokenCache("test tokens", LONG_CACHE_EXPIRATION, TimeUnit.SECONDS)
OtpAuthenticationToken andyToken = new OtpAuthenticationToken(andy)
OtpAuthenticationToken nathanToken = new OtpAuthenticationToken(nathan)
tokenCache.put(buildCacheKey(andyToken), andy)
tokenCache.put(buildCacheKey(nathanToken), nathan)
logger.info(tokenCache.toString())
// Act
boolean containsAndyToken = tokenCache.containsValue(andy)
boolean containsNathanToken = tokenCache.containsValue(nathan)
boolean containsMattToken = tokenCache.containsValue(matt)
// Assert
assert containsAndyToken
assert containsNathanToken
assert !containsMattToken
}
@Test
void testShouldGetKeyByValue() throws Exception {
// Arrange
TokenCache tokenCache = new TokenCache("test tokens", LONG_CACHE_EXPIRATION, TimeUnit.SECONDS)
OtpAuthenticationToken andyToken = new OtpAuthenticationToken(andy)
OtpAuthenticationToken nathanToken = new OtpAuthenticationToken(nathan)
tokenCache.put(buildCacheKey(andyToken), andy)
tokenCache.put(buildCacheKey(nathanToken), nathan)
logger.info(tokenCache.toString())
// Act
CacheKey keyForAndyToken = tokenCache.getKeyForValue(andy)
CacheKey keyForNathanToken = tokenCache.getKeyForValue(nathan)
CacheKey keyForMattToken = tokenCache.getKeyForValue(matt)
def tokens = [keyForAndyToken, keyForNathanToken, keyForMattToken]
logger.info("Retrieved tokens: ${tokens}")
// Assert
assert keyForAndyToken.getKey() == hash(andyToken.principal)
assert keyForNathanToken.getKey() == hash(nathanToken.principal)
assert !keyForMattToken
}
@Test
void testShouldNotGetKeyByValueAfterExpiration() throws Exception {
// Arrange
TokenCache tokenCache = new TokenCache("test tokens", SHORT_CACHE_EXPIRATION, TimeUnit.SECONDS)
OtpAuthenticationToken andyToken = new OtpAuthenticationToken(andy)
OtpAuthenticationToken nathanToken = new OtpAuthenticationToken(nathan)
tokenCache.put(buildCacheKey(andyToken), andy)
tokenCache.put(buildCacheKey(nathanToken), nathan)
logger.info(tokenCache.toString())
// Sleep to allow the cache entries to expire (was failing on Windows JDK 8 when only sleeping for 1 second)
sleep(SHORT_CACHE_EXPIRATION * 2 * 1000)
// Act
CacheKey keyForAndyToken = tokenCache.getKeyForValue(andy)
CacheKey keyForNathanToken = tokenCache.getKeyForValue(nathan)
CacheKey keyForMattToken = tokenCache.getKeyForValue(matt)
def tokens = [keyForAndyToken, keyForNathanToken, keyForMattToken]
logger.info("Retrieved tokens: ${tokens}")
// Assert
assert !keyForAndyToken
assert !keyForNathanToken
assert !keyForMattToken
}
@Test
void testShouldInvalidateSingleKey() throws Exception {
// Arrange
TokenCache tokenCache = new TokenCache("test tokens", LONG_CACHE_EXPIRATION, TimeUnit.SECONDS)
OtpAuthenticationToken andyToken = new OtpAuthenticationToken(andy)
OtpAuthenticationToken nathanToken = new OtpAuthenticationToken(nathan)
OtpAuthenticationToken mattToken = new OtpAuthenticationToken(matt)
CacheKey andyKey = buildCacheKey(andyToken)
CacheKey nathanKey = buildCacheKey(nathanToken)
CacheKey mattKey = buildCacheKey(mattToken)
tokenCache.put(andyKey, andy)
tokenCache.put(nathanKey, nathan)
tokenCache.put(mattKey, matt)
logger.info(tokenCache.toString())
// Act
tokenCache.invalidate(andyKey)
// Assert
assert !tokenCache.containsValue(andy)
assert tokenCache.containsValue(nathan)
assert tokenCache.containsValue(matt)
}
@Test
void testShouldInvalidateMultipleKeys() throws Exception {
// Arrange
TokenCache tokenCache = new TokenCache("test tokens", LONG_CACHE_EXPIRATION, TimeUnit.SECONDS)
OtpAuthenticationToken andyToken = new OtpAuthenticationToken(andy)
OtpAuthenticationToken nathanToken = new OtpAuthenticationToken(nathan)
OtpAuthenticationToken mattToken = new OtpAuthenticationToken(matt)
CacheKey andyKey = buildCacheKey(andyToken)
CacheKey nathanKey = buildCacheKey(nathanToken)
CacheKey mattKey = buildCacheKey(mattToken)
tokenCache.put(andyKey, andy)
tokenCache.put(nathanKey, nathan)
tokenCache.put(mattKey, matt)
logger.info(tokenCache.toString())
// Act
tokenCache.invalidateAll([andyKey, nathanKey])
// Assert
assert !tokenCache.containsValue(andy)
assert !tokenCache.containsValue(nathan)
assert tokenCache.containsValue(matt)
}
@Test
void testShouldInvalidateAll() throws Exception {
// Arrange
TokenCache tokenCache = new TokenCache("test tokens", LONG_CACHE_EXPIRATION, TimeUnit.SECONDS)
OtpAuthenticationToken andyToken = new OtpAuthenticationToken(andy)
OtpAuthenticationToken nathanToken = new OtpAuthenticationToken(nathan)
OtpAuthenticationToken mattToken = new OtpAuthenticationToken(matt)
CacheKey andyKey = buildCacheKey(andyToken)
CacheKey nathanKey = buildCacheKey(nathanToken)
CacheKey mattKey = buildCacheKey(mattToken)
tokenCache.put(andyKey, andy)
tokenCache.put(nathanKey, nathan)
tokenCache.put(mattKey, matt)
logger.info(tokenCache.toString())
// Act
tokenCache.invalidateAll()
// Assert
assert !tokenCache.containsValue(andy)
assert !tokenCache.containsValue(nathan)
assert !tokenCache.containsValue(matt)
}
}