diff --git a/src/zutil/net/http/page/oauth/OAuth2Registry.java b/src/zutil/net/http/page/oauth/OAuth2Registry.java index 06796b4..d2f1cd0 100644 --- a/src/zutil/net/http/page/oauth/OAuth2Registry.java +++ b/src/zutil/net/http/page/oauth/OAuth2Registry.java @@ -26,11 +26,10 @@ package zutil.net.http.page.oauth; import zutil.Hasher; import zutil.Timer; +import zutil.net.http.page.oauth.OAuth2RegistryStore.OAuth2ClientRegister; import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; -import java.util.Random; +import java.util.*; /** * A data class containing authentication information for individual @@ -40,13 +39,29 @@ public class OAuth2Registry implements Serializable { private static final long DEFAULT_CODE_TIMEOUT = 10 * 60 * 1000; // 10min private static final long DEFAULT_TOKEN_TIMEOUT = 24 * 60 * 60 * 1000; // 24h - private Map clientRegisters = new HashMap<>(); + private Map clientRegisters = new HashMap<>(); private boolean requireWhitelist = true; + transient private OAuth2RegistryStore registryStore; + transient private List tokenListeners = new ArrayList<>(); transient private Random random = new Random(); - transient private TokenRegistrationListener tokenListener; + /** + * Create an in memory only OAuth2 registry. + */ + public OAuth2Registry() {} + /** + * Create new OAuth2 registry with an offline backup store. + */ + public OAuth2Registry(OAuth2RegistryStore store) { + this.registryStore = store; + + for (OAuth2ClientRegister register : store.getClientRegistries()) { + clientRegisters.put(register.clientId, register); + } + } + // ------------------------------------------------------ // Whitelist methods // ------------------------------------------------------ @@ -70,7 +85,10 @@ public class OAuth2Registry implements Serializable { */ public void addWhitelist(String clientId) { if (!clientRegisters.containsKey(clientId)) { - clientRegisters.put(clientId, new ClientRegister()); + OAuth2ClientRegister clientRegister = new OAuth2ClientRegister(clientId); + + clientRegisters.put(clientId, clientRegister); + if (registryStore != null) registryStore.storeClientRegister(clientRegister); } } @@ -79,7 +97,7 @@ public class OAuth2Registry implements Serializable { // ------------------------------------------------------ /** - * Validates a client_id value is a valid value and is in the whitelist fro approved clients. + * Validates a client_id value is a valid value and is in the whitelist from approved clients. * * @param clientId the client_id value to validate * @return true if the client_id is allowed to start the OAuth process. @@ -94,33 +112,33 @@ public class OAuth2Registry implements Serializable { } /** - * Validates that a authorization code has valid format and has been authorized and not elapsed. + * Validates that an authorization code has valid format and has been authorized and not elapsed. * * @param code the code that should be validated * @return true if the given code is valid otherwise false. */ public boolean isAuthorizationCodeValid(String code) { - ClientRegister reg = getClientRegisterForAuthCode(code); + OAuth2ClientRegister clientRegister = getClientRegisterForAuthCode(code); - if (reg != null) { - return reg.authCodes.containsKey(code) && - !reg.authCodes.get(code).hasTimedOut(); + if (clientRegister != null) { + return clientRegister.authCodes.containsKey(code) && + !clientRegister.authCodes.get(code).hasTimedOut(); } return false; } /** - * Validates that a access token has valid format and has been authorized and not elapsed. + * Validates that an access token has valid format and has been authorized and not elapsed. * * @param token the token that should be validated * @return true if the given token is valid otherwise false. */ public boolean isAccessTokenValid(String token) { - ClientRegister reg = getClientRegisterForToken(token); + OAuth2ClientRegister clientRegister = getClientRegisterForToken(token); - if (reg != null) { - return reg.accessTokens.containsKey(token) && - !reg.accessTokens.get(token).hasTimedOut(); + if (clientRegister != null) { + return clientRegister.accessTokens.containsKey(token) && + !clientRegister.accessTokens.get(token).hasTimedOut(); } return false; } @@ -130,10 +148,11 @@ public class OAuth2Registry implements Serializable { // ------------------------------------------------------ public void revokeAuthorizationCode(String code) { - ClientRegister reg = getClientRegisterForAuthCode(code); + OAuth2ClientRegister clientRegister = getClientRegisterForAuthCode(code); - if (reg != null) { - reg.authCodes.remove(code); + if (clientRegister != null) { + clientRegister.authCodes.remove(code); + if (registryStore != null) registryStore.storeClientRegister(clientRegister); } } @@ -145,10 +164,12 @@ public class OAuth2Registry implements Serializable { return registerAuthorizationCode(clientId, code, DEFAULT_CODE_TIMEOUT); } protected long registerAuthorizationCode(String clientId, String code, long timeoutMillis) { - ClientRegister reg = getClientRegister(clientId); + OAuth2ClientRegister clientRegister = getClientRegister(clientId); - if (reg != null) { - reg.authCodes.put(code, new Timer(timeoutMillis).start()); + if (clientRegister != null) { + clientRegister.authCodes.put(code, new Timer(timeoutMillis).start()); + + if (registryStore != null) registryStore.storeClientRegister(clientRegister); return timeoutMillis; } return -1; @@ -158,13 +179,19 @@ public class OAuth2Registry implements Serializable { return registerAccessToken(clientId, token, DEFAULT_TOKEN_TIMEOUT); } protected long registerAccessToken(String clientId, String token, long timeoutMillis) { - ClientRegister reg = getClientRegister(clientId); + OAuth2ClientRegister clientRegister = getClientRegister(clientId); - if (reg != null) { - reg.accessTokens.put(token, new Timer(timeoutMillis).start()); + if (clientRegister != null) { + long absoluteTimeout = System.currentTimeMillis() + timeoutMillis; + clientRegister.accessTokens.put(token, new Timer(timeoutMillis).start()); - if (tokenListener != null) - tokenListener.onTokenRegistration(clientId, token, timeoutMillis); + if (registryStore != null) registryStore.storeClientRegister(clientRegister); + + if (tokenListeners != null) { + for (OAuth2TokenRegistrationListener listener : tokenListeners) { + listener.onTokenRegistration(clientId, token, absoluteTimeout); + } + } return timeoutMillis; } return -1; @@ -212,45 +239,46 @@ public class OAuth2Registry implements Serializable { // Listeners // ------------------------------------------------------ - public void setTokenListener(TokenRegistrationListener listener) { - this.tokenListener = listener; + public void addTokenListener(OAuth2TokenRegistrationListener listener) { + if (!this.tokenListeners.contains(listener)) + this.tokenListeners.add(listener); } - public interface TokenRegistrationListener { + public interface OAuth2TokenRegistrationListener { /** * Method will be called when a new token is successfully registered on a client * * @param clientId the client ID that got the specified token - * @param token a String token that has ben generated and provided to the client - * @param timeoutMillis the expiration time of the token + * @param token a String token that has been generated and provided to the client + * @param timeoutMillis the expiration epoc time of the token */ void onTokenRegistration(String clientId, String token, long timeoutMillis); } + // ------------------------------------------------------ + // Client register logic // ------------------------------------------------------ - private ClientRegister getClientRegister(String clientId) { - if (!requireWhitelist && !clientRegisters.containsKey(clientId)) - clientRegisters.put(clientId, new ClientRegister()); + private OAuth2ClientRegister getClientRegister(String clientId) { + if (!requireWhitelist && !clientRegisters.containsKey(clientId)) { + OAuth2ClientRegister clientRegister = new OAuth2ClientRegister(clientId); + + clientRegisters.put(clientId, clientRegister); + if (registryStore != null) registryStore.storeClientRegister(clientRegister); + } return clientRegisters.get(clientId); } - private ClientRegister getClientRegisterForAuthCode(String code) { + private OAuth2ClientRegister getClientRegisterForAuthCode(String code) { String clientId = getClientIdForAuthenticationCode(code); return (clientId == null ? null : clientRegisters.get(clientId)); } - private ClientRegister getClientRegisterForToken(String token) { + private OAuth2ClientRegister getClientRegisterForToken(String token) { String clientId = getClientIdForAccessToken(token); return (clientId == null ? null : clientRegisters.get(clientId)); } - - - private static class ClientRegister implements Serializable { - Map authCodes = new HashMap<>(); - Map accessTokens = new HashMap<>(); - } } diff --git a/src/zutil/net/http/page/oauth/OAuth2RegistryStore.java b/src/zutil/net/http/page/oauth/OAuth2RegistryStore.java new file mode 100644 index 0000000..a3bf0dd --- /dev/null +++ b/src/zutil/net/http/page/oauth/OAuth2RegistryStore.java @@ -0,0 +1,57 @@ +package zutil.net.http.page.oauth; + +import zutil.Timer; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; + + +/** + * A storage interface for the {@link OAuth2Registry} class. This interface + * is mainly used to save any changed client states and to load client data. + */ +public interface OAuth2RegistryStore { + + /** + * @return all previously stored client registries. + */ + List getClientRegistries(); + + /** + * A client registry has been updated and needs to be stored for later retrieval. + * + * @param register the register object that has changed and needs to be stored. + */ + void storeClientRegister(OAuth2ClientRegister register); + + + /** + * This data class contains OAuth data related to a single client. + */ + class OAuth2ClientRegister implements Serializable { + public String clientId; + public HashMap authCodes = new HashMap<>(); + public HashMap accessTokens = new HashMap<>(); + + + public OAuth2ClientRegister(String clientId) { + this.clientId = clientId; + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + OAuth2ClientRegister that = (OAuth2ClientRegister) o; + return Objects.equals(clientId, that.clientId); + } + + @Override + public int hashCode() { + return Objects.hash(clientId); + } + } +} diff --git a/src/zutil/parser/json/JSONObjectInputStream.java b/src/zutil/parser/json/JSONObjectInputStream.java index 2ea4975..03e63ea 100755 --- a/src/zutil/parser/json/JSONObjectInputStream.java +++ b/src/zutil/parser/json/JSONObjectInputStream.java @@ -25,6 +25,8 @@ package zutil.parser.json; import zutil.ClassUtil; +import zutil.io.StringInputStream; +import zutil.io.StringOutputStream; import zutil.log.LogUtil; import zutil.parser.Base64Decoder; import zutil.parser.DataNode; @@ -55,6 +57,23 @@ public class JSONObjectInputStream extends InputStream implements ObjectInput, C } + /** + * @return a String containing the JSON representation of the Object + */ + public static T parse(String json) { + try { + StringInputStream in = new StringInputStream(); + JSONObjectInputStream reader = new JSONObjectInputStream(in); + T object = reader.readGenericObject(); + reader.close(); + return object; + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + /** * If no metadata is available in the stream then this * class will be instantiated and assigned data from the received JSON. diff --git a/src/zutil/parser/json/JSONObjectOutputStream.java b/src/zutil/parser/json/JSONObjectOutputStream.java index ab6789f..c47f993 100755 --- a/src/zutil/parser/json/JSONObjectOutputStream.java +++ b/src/zutil/parser/json/JSONObjectOutputStream.java @@ -79,6 +79,7 @@ public class JSONObjectOutputStream extends OutputStream implements ObjectOutput return null; } + public synchronized void writeObject(Object obj) throws IOException{ try { out.write(getDataNode(obj));