Added a OAuth2 Store to save state between application restarts
This commit is contained in:
parent
8ba1441572
commit
8508df42ac
4 changed files with 149 additions and 44 deletions
|
|
@ -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<String, ClientRegister> clientRegisters = new HashMap<>();
|
||||
private Map<String, OAuth2ClientRegister> clientRegisters = new HashMap<>();
|
||||
private boolean requireWhitelist = true;
|
||||
|
||||
transient private OAuth2RegistryStore registryStore;
|
||||
transient private List<OAuth2TokenRegistrationListener> 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<String, Timer> authCodes = new HashMap<>();
|
||||
Map<String, Timer> accessTokens = new HashMap<>();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
57
src/zutil/net/http/page/oauth/OAuth2RegistryStore.java
Normal file
57
src/zutil/net/http/page/oauth/OAuth2RegistryStore.java
Normal file
|
|
@ -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<OAuth2ClientRegister> 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<String, Timer> authCodes = new HashMap<>();
|
||||
public HashMap<String, Timer> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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> 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.
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue