io.warp10.quasar.trl.QuasarTokenRevocationListLoader Maven / Gradle / Ivy
//
// Copyright 2018-2023 SenX S.A.S.
//
// 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 io.warp10.quasar.trl;
import io.warp10.crypto.SipHashInline;
import io.warp10.quasar.filter.QuasarConfiguration;
import io.warp10.quasar.filter.sensision.QuasarTokenFilterSensisionConstants;
import io.warp10.sensision.Sensision;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.bouncycastle.util.encoders.Hex;
public class QuasarTokenRevocationListLoader {
Properties config = null;
private static AtomicBoolean initialized = new AtomicBoolean(false);
private static long delay = 0L;
private static String path = null;
private static QuasarTRL currentTrl = null;
private static QuasarTokenRevocationListLoader quasarTokenRevocationListLoader = null;
private static AtomicBoolean singleton = new AtomicBoolean(false);
private static long appIdSipHashKeyK0 = 0L;
private static long appIdSipHashKeyK1 = 0L;
private List quasarTRLLoadedHandler = new ArrayList();
private String trlPattern = "^([a-zA-Z0-9_-]*)\\.(read|write|full)\\.([0-9]*)-([a-f0-9]{32})\\.trl$";
// Set of files already read
private Map read = new HashMap();
private Map labels = new HashMap();
public static synchronized QuasarTokenRevocationListLoader getInstance(Properties config, byte[] appSipHashKey) {
if (singleton.compareAndSet(false, true)) {
ByteBuffer bb = ByteBuffer.wrap(appSipHashKey);
bb.order(ByteOrder.BIG_ENDIAN);
appIdSipHashKeyK0 = bb.getLong();
appIdSipHashKeyK1 = bb.getLong();
quasarTokenRevocationListLoader = new QuasarTokenRevocationListLoader(config);
}
return quasarTokenRevocationListLoader;
}
private QuasarTokenRevocationListLoader(Properties props) {
this.config = props;
delay = Long.parseLong(config.getProperty(QuasarConfiguration.WARP_TRL_PERIOD, QuasarConfiguration.WARP_TRL_PERIOD_DEFAULT));
path = config.getProperty(QuasarConfiguration.WARP_TRL_PATH);
}
public static long getApplicationHash(String appName) {
if (appName != null && appName.length() > 0) {
byte[] appNameByteArray = appName.getBytes();
return SipHashInline.hash24(appIdSipHashKeyK0, appIdSipHashKeyK1, appNameByteArray, 0, appNameByteArray.length);
}
return 0L;
}
public void loadTrl() {
try {
QuasarTRL quasarTRL = null;
//
// Sensision metrics thread heart beat
//
Sensision.event(QuasarTokenFilterSensisionConstants.SENSISION_CLASS_QUASAR_FILTER_TRL_COUNT, labels, 1);
//
// get all files in the directory
//
String[] files = getFolderFiles(path);
//
// extract the most recent files per warp.type
//
Map latest = latestFilesToRead(files);
boolean update = updateTRL(read, latest);
if (update) {
long now = System.currentTimeMillis();
// sum files size
int size = getSipHashesSize(latest.values());
// load the selected files
for (Map.Entry entry: latest.entrySet()) {
if (null == quasarTRL) {
quasarTRL = new QuasarTRL(size);
}
//
// Read the token revocation list
//
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(new File(path, entry.getValue().fileName)));
while (true) {
String line = br.readLine();
if (null == line) {
break;
}
line = line.trim();
// Skip empty lines
if ("".equals(line)) {
continue;
}
// Skip comments
if (line.startsWith("#")) {
continue;
}
// application
if (line.startsWith(QuasarConfiguration.WARP_APPLICATION_PREFIX)) {
// compute the sip hash with the app name
long appSipHash = getApplicationHash(line.substring(1));
quasarTRL.revokeApplication(appSipHash);
} else {
// token sip hash hex encoded convert it into long
byte[] bytes = Hex.decode(line);
long tokenRevoked = ByteBuffer.wrap(bytes, 0, 8).order(ByteOrder.BIG_ENDIAN).getLong();
// add it to the future trl list
quasarTRL.revokeToken(tokenRevoked);
}
}
// mark as read
read.put(entry.getKey(), entry.getValue());
} catch (Exception exp) {
exp.printStackTrace();
} finally {
if (null != br) {
try {
br.close();
} catch (IOException e) {
}
}
}
} // end for all files
if (0 != quasarTRLLoadedHandler.size() && null != quasarTRL) {
//
// sort and switch the new trl
//
quasarTRL.sortTokens();
//
// call all the handlers
//
for (QuasarTRLLoadedHandler handler: quasarTRLLoadedHandler) {
handler.onQuasarTRL(quasarTRL);
}
currentTrl = quasarTRL;
//
// Sensision trl loaded
//
long timeElapsed = System.currentTimeMillis() - now;
Sensision.event(QuasarTokenFilterSensisionConstants.SENSISION_CLASS_QUASAR_FILTER_TRL_LOAD_TIME, labels, timeElapsed);
Sensision.event(QuasarTokenFilterSensisionConstants.SENSISION_CLASS_QUASAR_FILTER_TRL_TOKENS_COUNT, labels, quasarTRL.getTrlSize());
}
} // end if update
} catch (Exception exp) {
// thread error
Sensision.update(QuasarTokenFilterSensisionConstants.SENSISION_CLASS_QUASAR_FILTER_TRL_ERROR_COUNT, labels, 1);
}
}
public void init() {
// initialize only once per JVM
if (initialized.get()) {
return;
}
Thread t = new Thread() {
@Override
public void run() {
while (true) {
loadTrl();
// time to sleep
try {
Thread.sleep(delay);
} catch (InterruptedException ie) {
}
} // while(true)
} // run()
};
if (null != path && initialized.compareAndSet(false, true)) {
t.setName("[TokenRevocationListLoader]");
t.setDaemon(true);
t.start();
}
}
private boolean updateTRL(Map read, Map latest) {
boolean update = false;
for (Map.Entry keyAndNewTrl: latest.entrySet()) {
JavaTRLLoaded actualTrl = read.get(keyAndNewTrl.getKey());
JavaTRLLoaded newTrl = keyAndNewTrl.getValue();
// not current trl -> load it
if (null == actualTrl) {
update = true;
break;
}
// md5 not equals -> load it
if (!actualTrl.md5.equals(newTrl.md5)) {
update = true;
break;
}
}
return update;
}
private Map latestFilesToRead(String[] files) {
// key = warp.type
Map filesToRead = new HashMap();
Pattern pattern = Pattern.compile(trlPattern);
for (String file: files) {
Matcher matcher = pattern.matcher(file);
if (matcher.matches()) {
// get the key warp.type
String warp = matcher.group(1);
String type = matcher.group(2);
long ts = Long.valueOf(matcher.group(3));
String md5 = matcher.group(4);
String key = warp + "." + type;
JavaTRLLoaded current = filesToRead.get(key);
if (null == current || (null != current && ts > current.timestamp)) {
JavaTRLLoaded next = new JavaTRLLoaded();
next.fileName = file;
next.timestamp = ts;
next.warp = warp;
next.type = type;
next.md5 = md5;
filesToRead.put(key, next);
}
}
}
return filesToRead;
}
private String[] getFolderFiles(String path) {
final File root = new File(path);
String[] files = root.list(new FilenameFilter() {
@Override
public boolean accept(File d, String name) {
if (!d.equals(root)) {
return false;
}
return name.matches(trlPattern);
}
});
// Sort files in lexicographic order
if (null == files) {
files = new String[0];
}
Arrays.sort(files);
return files;
}
/**
* Estimation if the number of SIPhashes in the files according to the file size
* @param files
* @return
*/
private int getSipHashesSize(Collection files) {
// sum files size
int size = 0;
for (JavaTRLLoaded file: files) {
File filename = new File(path, file.fileName);
size += filename.length();
}
// each line = long hexa encoded (16 bytes) + CR
return size / 17;
}
public void addTrlUpdatedHandler(QuasarTRLLoadedHandler handler) {
quasarTRLLoadedHandler.add(handler);
// notify if a trl is already available
if (null != currentTrl) {
handler.onQuasarTRL(currentTrl);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy