com.netflix.msl.util.SimpleMslStore Maven / Gradle / Ivy
/**
* Copyright (c) 2012-2014 Netflix, Inc. All rights reserved.
*
* 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 com.netflix.msl.util;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import com.netflix.msl.MslConstants;
import com.netflix.msl.MslError;
import com.netflix.msl.MslException;
import com.netflix.msl.MslInternalException;
import com.netflix.msl.crypto.ICryptoContext;
import com.netflix.msl.tokens.MasterToken;
import com.netflix.msl.tokens.ServiceToken;
import com.netflix.msl.tokens.UserIdToken;
/**
* A simple MSL store that maintains state.
*
* This class is thread-safe.
*
* @author Wesley Miaw
*/
public class SimpleMslStore implements MslStore {
/**
* Increments the provided non-replayable ID by 1, wrapping around to zero
* if the provided value is equal to {@link MslConstants#MAX_LONG_VALUE}.
*
* @param id the non-replayable ID to increment.
* @return the non-replayable ID + 1.
* @throws MslInternalException if the provided non-replayable ID is out of
* range.
*/
private static long incrementNonReplayableId(final long id) {
if (id < 0 || id > MslConstants.MAX_LONG_VALUE)
throw new MslInternalException("Non-replayable ID " + id + " is outside the valid range.");
return (id == MslConstants.MAX_LONG_VALUE) ? 0 : id + 1;
}
/* (non-Javadoc)
* @see com.netflix.msl.util.MslStore#setCryptoContext(com.netflix.msl.tokens.MasterToken, com.netflix.msl.crypto.ICryptoContext)
*/
@Override
public void setCryptoContext(final MasterToken masterToken, final ICryptoContext cryptoContext) {
if (cryptoContext == null)
removeCryptoContext(masterToken);
else
cryptoContexts.put(masterToken, cryptoContext);
}
/* (non-Javadoc)
* @see com.netflix.msl.util.MslStore#getMasterToken()
*/
@Override
public MasterToken getMasterToken() {
MasterToken masterToken = null;
for (final MasterToken storedMasterToken : cryptoContexts.keySet()) {
if (masterToken == null || storedMasterToken.isNewerThan(masterToken))
masterToken = storedMasterToken;
}
return masterToken;
}
/* (non-Javadoc)
* @see com.netflix.msl.util.MslStore#getNonReplayableId(com.netflix.msl.tokens.MasterToken)
*/
@Override
public synchronized long getNonReplayableId(final MasterToken masterToken) {
// Return the next largest non-replayable ID, or 1 if there is none.
final long serialNumber = masterToken.getSerialNumber();
final long currentId = (nonReplayableIds.containsKey(serialNumber))
? nonReplayableIds.get(serialNumber)
: 0;
final long nextId = incrementNonReplayableId(currentId);
nonReplayableIds.put(serialNumber, nextId);
return nextId;
}
/* (non-Javadoc)
* @see com.netflix.msl.util.MslStore#getCryptoContext(com.netflix.msl.tokens.MasterToken)
*/
@Override
public ICryptoContext getCryptoContext(final MasterToken masterToken) {
return cryptoContexts.get(masterToken);
}
/* (non-Javadoc)
* @see com.netflix.msl.util.MslStore#removeCryptoContext(com.netflix.msl.tokens.MasterToken)
*/
@Override
public synchronized void removeCryptoContext(final MasterToken masterToken) {
if (cryptoContexts.remove(masterToken) != null) {
// Remove bound user ID tokens, service tokens, and the non-
// replayable ID if we no longer have a master token with the same
// serial number.
final long serialNumber = masterToken.getSerialNumber();
for (final MasterToken token : cryptoContexts.keySet()) {
if (token.getSerialNumber() == serialNumber)
return;
}
// Remove the non-replayable ID.
nonReplayableIds.remove(serialNumber);
// Remove bound user ID tokens and service tokens.
for (final UserIdToken userIdToken : userIdTokens.values()) {
if (userIdToken.isBoundTo(masterToken))
removeUserIdToken(userIdToken);
}
try {
removeServiceTokens(null, masterToken, null);
} catch (final MslException e) {
// This should not happen since we are only providing a master
// token.
throw new MslInternalException("Unexpected exception while removing master token bound service tokens.", e);
}
}
}
/* (non-Javadoc)
* @see com.netflix.msl.util.MslStore#clearCryptoContexts()
*/
@Override
public synchronized void clearCryptoContexts() {
cryptoContexts.clear();
nonReplayableIds.clear();
userIdTokens.clear();
uitServiceTokens.clear();
mtServiceTokens.clear();
}
/* (non-Javadoc)
* @see com.netflix.msl.util.MslStore#addUserIdToken(java.lang.String, com.netflix.msl.tokens.UserIdToken)
*/
@Override
public void addUserIdToken(final String userId, final UserIdToken userIdToken) throws MslException {
boolean foundMasterToken = false;
for (final MasterToken masterToken : cryptoContexts.keySet()) {
if (userIdToken.isBoundTo(masterToken)) {
foundMasterToken = true;
break;
}
}
if (!foundMasterToken)
throw new MslException(MslError.USERIDTOKEN_MASTERTOKEN_NOT_FOUND, "uit mtserialnumber " + userIdToken.getMasterTokenSerialNumber());
userIdTokens.put(userId, userIdToken);
}
/* (non-Javadoc)
* @see com.netflix.msl.util.MslStore#getUserIdToken(java.lang.String)
*/
@Override
public UserIdToken getUserIdToken(final String userId) {
return userIdTokens.get(userId);
}
/* (non-Javadoc)
* @see com.netflix.msl.util.MslStore#removeUserIdToken(com.netflix.msl.tokens.UserIdToken)
*/
@Override
public void removeUserIdToken(final UserIdToken userIdToken) {
// Find the master token this user ID token is bound to.
MasterToken masterToken = null;
for (final MasterToken token : cryptoContexts.keySet()) {
if (userIdToken.isBoundTo(token)) {
masterToken = token;
break;
}
}
// If we didn't find a master token we shouldn't be able to find a user
// ID token, but it doesn't hurt to try anyway and clean things up.
for (final Entry entry : userIdTokens.entrySet()) {
if (entry.getValue().equals(userIdToken)) {
final String userId = entry.getKey();
userIdTokens.remove(userId);
try {
removeServiceTokens(null, masterToken, userIdToken);
} catch (final MslException e) {
// This should not happen since we have already confirmed
// that the user ID token is bound to the master token.
throw new MslInternalException("Unexpected exception while removing user ID token bound service tokens.", e);
}
break;
}
}
}
/* (non-Javadoc)
* @see com.netflix.msl.util.MslStore#clearUserIdTokens()
*/
@Override
public void clearUserIdTokens() {
for (final UserIdToken userIdToken : userIdTokens.values()) {
try {
removeServiceTokens(null, null, userIdToken);
} catch (final MslException e) {
// This should not happen since we are only providing a user ID
// token.
throw new MslInternalException("Unexpected exception while removing user ID token bound service tokens.", e);
}
}
userIdTokens.clear();
}
/* (non-Javadoc)
* @see com.netflix.msl.util.MslStore#addServiceTokens(java.util.Set)
*/
@Override
public synchronized void addServiceTokens(final Set tokens) throws MslException {
// Verify we recognize the bound service tokens.
for (final ServiceToken token : tokens) {
// Verify master token bound.
if (token.isMasterTokenBound()) {
boolean foundMasterToken = false;
for (final MasterToken masterToken : cryptoContexts.keySet()) {
if (token.isBoundTo(masterToken)) {
foundMasterToken = true;
break;
}
}
if (!foundMasterToken)
throw new MslException(MslError.SERVICETOKEN_MASTERTOKEN_NOT_FOUND, "st mtserialnumber " + token.getMasterTokenSerialNumber());
}
// Verify user token bound.
if (token.isUserIdTokenBound()) {
boolean foundUserIdToken = false;
for (final UserIdToken userIdToken : userIdTokens.values()) {
if (token.isBoundTo(userIdToken)) {
foundUserIdToken = true;
break;
}
}
if (!foundUserIdToken)
throw new MslException(MslError.SERVICETOKEN_USERIDTOKEN_NOT_FOUND, "st uitserialnumber " + token.getUserIdTokenSerialNumber());
}
}
// Add service tokens.
for (final ServiceToken token : tokens) {
// Unbound?
if (token.isUnbound()) {
unboundServiceTokens.add(token);
continue;
}
// Master token bound?
if (token.isMasterTokenBound()) {
Set tokenSet = mtServiceTokens.get(token.getMasterTokenSerialNumber());
if (tokenSet == null) {
tokenSet = new HashSet();
mtServiceTokens.put(token.getMasterTokenSerialNumber(), tokenSet);
}
tokenSet.add(token);
}
// User ID token bound?
if (token.isUserIdTokenBound()) {
Set tokenSet = uitServiceTokens.get(token.getUserIdTokenSerialNumber());
if (tokenSet == null) {
tokenSet = new HashSet();
uitServiceTokens.put(token.getUserIdTokenSerialNumber(), tokenSet);
}
tokenSet.add(token);
}
}
}
/* (non-Javadoc)
* @see com.netflix.msl.util.MslStore#getServiceTokens(com.netflix.msl.tokens.MasterToken, com.netflix.msl.tokens.UserIdToken)
*/
@Override
public synchronized Set getServiceTokens(final MasterToken masterToken, final UserIdToken userIdToken) throws MslException {
// Validate arguments.
if (userIdToken != null) {
if (masterToken == null)
throw new MslException(MslError.USERIDTOKEN_MASTERTOKEN_NULL);
if (!userIdToken.isBoundTo(masterToken))
throw new MslException(MslError.USERIDTOKEN_MASTERTOKEN_MISMATCH, "uit mtserialnumber " + userIdToken.getMasterTokenSerialNumber() + "; mt " + masterToken.getSerialNumber());
}
// Grab service tokens. We start with the set of unbound service
// tokens.
final Set serviceTokens = new HashSet();
serviceTokens.addAll(unboundServiceTokens);
// If we have a master token add the set of master token bound service
// tokens that are not bound to any user ID tokens.
if (masterToken != null) {
final Set mtTokens = mtServiceTokens.get(masterToken.getSerialNumber());
if (mtTokens != null) {
for (final ServiceToken mtToken : mtTokens) {
if (!mtToken.isUserIdTokenBound())
serviceTokens.add(mtToken);
}
}
}
// If we have a user ID token (and because of the check above a master
// token) add the set of user ID token bound service tokens that are
// also bound to the same master token.
if (userIdToken != null) {
final Set uitTokens = uitServiceTokens.get(userIdToken.getSerialNumber());
if (uitTokens != null) {
for (final ServiceToken uitToken : uitTokens) {
if (uitToken.isBoundTo(masterToken))
serviceTokens.add(uitToken);
}
}
}
return serviceTokens;
}
/* (non-Javadoc)
* @see com.netflix.msl.util.MslStore#removeServiceTokens(java.lang.String, com.netflix.msl.tokens.MasterToken, com.netflix.msl.tokens.UserIdToken)
*/
@Override
public synchronized void removeServiceTokens(final String name, final MasterToken masterToken, final UserIdToken userIdToken) throws MslException {
// Validate arguments.
if (userIdToken != null && masterToken != null &&
!userIdToken.isBoundTo(masterToken))
{
throw new MslException(MslError.USERIDTOKEN_MASTERTOKEN_MISMATCH, "uit mtserialnumber " + userIdToken.getMasterTokenSerialNumber() + "; mt " + masterToken.getSerialNumber());
}
// If only a name was provided remove all tokens with that name.
if (name != null && masterToken == null && userIdToken == null) {
// Remove all unbound tokens with the specified name.
final Iterator unboundTokens = unboundServiceTokens.iterator();
while (unboundTokens.hasNext()) {
if (unboundTokens.next().getName().equals(name))
unboundTokens.remove();
}
// Remove all master bound tokens with the specified name.
final Collection>> mtTokenEntries = mtServiceTokens.entrySet();
for (final Entry> entry : mtTokenEntries) {
final Long serialNumber = entry.getKey();
final Set tokenSet = entry.getValue();
final Iterator tokens = tokenSet.iterator();
while (tokens.hasNext()) {
final ServiceToken token = tokens.next();
// Skip if the name was provided and it does not match.
if (!token.getName().equals(name))
continue;
// Remove the token.
tokens.remove();
}
mtServiceTokens.put(serialNumber, tokenSet);
}
// Remove all user ID tokens with the specified name.
final Collection>> uitTokenEntries = uitServiceTokens.entrySet();
for (final Entry> entry : uitTokenEntries) {
final Long serialNumber = entry.getKey();
final Set tokenSet = entry.getValue();
final Iterator tokens = tokenSet.iterator();
while (tokens.hasNext()) {
final ServiceToken token = tokens.next();
// Skip if the name was provided and it does not match.
if (!token.getName().equals(name))
continue;
// Remove the token.
tokens.remove();
}
uitServiceTokens.put(serialNumber, tokenSet);
}
}
// If a master token was provided but no user ID token was provided,
// remove all tokens bound to the master token. If a name was also
// provided then limit removal to tokens with the specified name.
if (masterToken != null && userIdToken == null) {
final Set tokenSet = mtServiceTokens.get(masterToken.getSerialNumber());
if (tokenSet != null) {
final Iterator tokens = tokenSet.iterator();
while (tokens.hasNext()) {
final ServiceToken token = tokens.next();
// Skip if the name was provided and it does not match.
if (name != null && !token.getName().equals(name))
continue;
// Remove the token.
tokens.remove();
}
}
// Remove all user ID tokens (with the specified name if any).
final Collection>> entries = uitServiceTokens.entrySet();
for (final Entry> entry : entries) {
final Long serialNumber = entry.getKey();
final Set uitTokenSet = entry.getValue();
final Iterator tokens = uitTokenSet.iterator();
while (tokens.hasNext()) {
final ServiceToken token = tokens.next();
// Skip if the name was provided and it does not match.
if (name != null && !token.getName().equals(name))
continue;
// Skip if the token is not bound to the master token.
if (!token.isBoundTo(masterToken))
continue;
// Remove the token.
tokens.remove();
}
uitServiceTokens.put(serialNumber, uitTokenSet);
}
}
// If a user ID token was provided remove all tokens bound to the user
// ID token. If a name was also provided then limit removal to tokens
// with the specified name. If a master token was also provided then
// limit removal to tokens bound to the master token.
if (userIdToken != null) {
final Set tokenSet = uitServiceTokens.get(userIdToken.getSerialNumber());
if (tokenSet != null) {
final Iterator tokens = tokenSet.iterator();
while (tokens.hasNext()) {
final ServiceToken token = tokens.next();
// Skip if the name was provided and it does not match.
if (name != null && !token.getName().equals(name))
continue;
// Skip if the master token was provided and the token is
// not bound to it.
if (masterToken != null && !token.isBoundTo(masterToken))
continue;
// Remove the token.
tokens.remove();
}
}
}
}
/* (non-Javadoc)
* @see com.netflix.msl.util.MslStore#clearServiceTokens()
*/
@Override
public synchronized void clearServiceTokens() {
unboundServiceTokens.clear();
mtServiceTokens.clear();
uitServiceTokens.clear();
}
/** Map of master tokens onto crypto contexts. */
private final Map cryptoContexts = new ConcurrentHashMap();
/** Map of local user IDs onto User ID tokens. */
private final Map userIdTokens = new ConcurrentHashMap();
/** Map of master token serial numbers onto non-replayable IDs. */
private final Map nonReplayableIds = new HashMap();
/** Set of unbound service tokens. */
private final Set unboundServiceTokens = new HashSet();
/** Map of master token serial numbers onto service tokens. */
private final Map> mtServiceTokens = new HashMap>();
/** Map of user ID token serial numbers onto service tokens. */
private final Map> uitServiceTokens = new HashMap>();
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy