001package jmri.server.json; 002 003import java.util.HashMap; 004import java.util.Map; 005import java.util.UUID; 006 007import javax.annotation.Nonnull; 008import javax.annotation.CheckForNull; 009 010import jmri.InstanceManager; 011import jmri.InstanceManagerAutoDefault; 012 013/** 014 * Manager for deletion tokens in the JSON protocols. This is a separate manager 015 * to be able to support the RESTful API where the connection where a token is 016 * generated may be broken before the client's deletion request containing the 017 * token is sent. 018 * <p> 019 * Note this is <em>package private</em> and is not part of a committed to API. 020 * 021 * @author Randall Wood Copyright 2019 022 * @since 4.15.6 023 */ 024class JsonDeleteTokenManager { 025 026 private final Map<String, String> tokens = new HashMap<>(); 027 028 /** 029 * Use this method to access the default instance. This ensures that public 030 * API does not need to be exposed for {@link InstanceManagerAutoDefault} to 031 * function. 032 * 033 * @return the default instance 034 */ 035 static JsonDeleteTokenManager getDefault() { 036 if (InstanceManager.getNullableDefault(JsonDeleteTokenManager.class) == null) { 037 InstanceManager.setDefault(JsonDeleteTokenManager.class, new JsonDeleteTokenManager()); 038 } 039 return InstanceManager.getDefault(JsonDeleteTokenManager.class); 040 } 041 042 JsonDeleteTokenManager() { 043 // nothing to do 044 } 045 046 /** 047 * Accept a token. If the token is not valid, any valid token is also invalidated. 048 * 049 * @param type the type of the object to delete 050 * @param name the name of the object to delete 051 * @param token the token to test 052 * @return true if the token was accepted; false otherwise 053 */ 054 boolean acceptToken(@Nonnull String type, @Nonnull String name, @CheckForNull String token) { 055 // generate a random token so that a fixed string cannot be discovered and used to 056 // bypass this check 057 String value = tokens.getOrDefault(getKey(type, name), UUID.randomUUID().toString()); 058 return value.equals(token); 059 } 060 061 /** 062 * Generate a token to allow deletion following the rejection of a deletion 063 * request. 064 * 065 * @param type the type of the object to delete 066 * @param name the name of the object to delete 067 * @return the token to use to confirm a deletion should be accepted 068 */ 069 String getToken(@Nonnull String type, @Nonnull String name) { 070 String key = getKey(type, name); 071 tokens.put(key, UUID.randomUUID().toString()); 072 return tokens.get(key); 073 } 074 075 private String getKey(@Nonnull String type, @Nonnull String name) { 076 return type + name; 077 } 078}