From 6713e54d674bd6aa858065fa27eda91987cf0c45 Mon Sep 17 00:00:00 2001 From: yvesn <yvesn> Date: Wed, 2 Aug 2017 15:38:49 +0000 Subject: [PATCH] SSDM-5398: password reset - storing tokens in file; added timestamp to make sure the token in not older then 2 days SVN: 38598 --- .../PersistentKeyValueStore.java | 75 ++++++++++++++++-- .../lib/persistentkeyvaluestore.jar | Bin 974 -> 3462 bytes .../password-reset-api/password-reset-api.py | 15 +++- .../password-reset-api/plugin.properties | 1 + 4 files changed, 81 insertions(+), 10 deletions(-) diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/lib/persistentkeyvaluestore-source/PersistentKeyValueStore.java b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/lib/persistentkeyvaluestore-source/PersistentKeyValueStore.java index c1aec05d6bb..cbbd3a4b2f7 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/lib/persistentkeyvaluestore-source/PersistentKeyValueStore.java +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/lib/persistentkeyvaluestore-source/PersistentKeyValueStore.java @@ -1,27 +1,88 @@ package ch.ethz.sis; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; +import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import ch.systemsx.cisd.openbis.dss.generic.server.DataStoreServer; public class PersistentKeyValueStore { + + private static final String KEY_STORE_FILE; private static ConcurrentMap<String, Serializable> keyStore = new ConcurrentHashMap<>(); - public static void put(String key, Serializable value) { + static { + Properties properties = DataStoreServer.getConfigParameters().getProperties(); + String storerootDir = properties.getProperty("storeroot-dir"); + KEY_STORE_FILE = storerootDir + "/" + "PersistentKeyValueStore.bin"; + load(); + } + + // + // Public API + // + public synchronized static void put(String key, Serializable value) { keyStore.put(key, value); + save(); + print(); } - - public static Serializable get(String key) { + + public synchronized static Serializable get(String key) { + print(); return keyStore.get(key); } - - public static void remove(String key) { + + public synchronized static void remove(String key) { keyStore.remove(key); + save(); } - - public static boolean containsKey(String key) { + + public synchronized static boolean containsKey(String key) { + print(); return keyStore.containsKey(key); } + + private synchronized static void print() { + for (String key : keyStore.keySet()) { + System.out.println(key + " - " + keyStore.get(key)); + } + } + + // + // save / load + // + private static void save() { + try (FileOutputStream fos = new FileOutputStream(KEY_STORE_FILE)) + { + ObjectOutputStream oos = new ObjectOutputStream(fos); + oos.writeObject(keyStore); + oos.close(); + } catch (IOException e) + { + e.printStackTrace(); + } + } + + @SuppressWarnings("unchecked") + private static void load() { + try (FileInputStream fis = new FileInputStream(KEY_STORE_FILE)) { + if (new File(KEY_STORE_FILE).exists()) { + ObjectInputStream ois = new ObjectInputStream(fis); + keyStore = (ConcurrentMap<String, Serializable>) ois.readObject(); + ois.close(); + } + } catch (IOException | ClassNotFoundException e) + { + e.printStackTrace(); + } + } + } diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/lib/persistentkeyvaluestore.jar b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/lib/persistentkeyvaluestore.jar index d3f77f6a23766b0947ddd07d80311cdfe1069101..1f2a896f4205e7371109844e1328390ff6557c8f 100644 GIT binary patch literal 3462 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 JMc8Poe*rY!-$wud delta 742 zcmZpZKF7`%;LXe;!ob17!5~%n-)kaYEQ=_Ro?OdhS>HS5cD|6K$g%y6K?#w!7U^z_ z%HJF55UFpPrBJ)lQ0-_|)4?0tQ|BJI@-}btn@L;hC-cY2&B{uu`POeVZ)U7!qiHL9 z+P5=5>z-Q|i`D)A`A_(OnB4M-2@zX&v3=jI?&klc$NbLXqtE;HpYD%dCi`L8`zo7W z&9<G)oAaf%y)5aezy6jv?<dd9{y!0_25U^O6h7d-y&>i}mrcLMv2zK(PuZ+8Oqspt z@r*hJ)oNjnSG}KC`Q8`yTQ26-l5Mh6-6wj^jNIARw{gx}8NIEbW&ha;cXYN&EGxUi zpJI1@L!sl!>1pe|pPIawan+E$H*wdxh)9E&OA`;79L{mJsL{8`DSltSc8!-h?{%ZM z(>(oNM6#Ny7<zC0yY7YI8gsQB@t@||aJ_KSo;~?nbk@@X-s_hm(js>LkSo=B#CG^{ zVx7g&TNO)8<dm9CvTwehVc!-KUv?;<XS>%ezU9wO8J?Z`NOjY8UfI<<oELY0lzD8> zD|EO<rq}J|N9XPSvs*2=U#Y!+sZoDA%W{|FeG$L>@NLqw_v?R_f3f1xLyu$j<-d8N zF7w)S?-$tg?^FG=xT!&RmYM5#?)MNndA;YFiLL5{g|%#vU6XXXLNwIucgkP5KR@Z2 zG*^7cqtsv1i~?Tm>GHU7$06f_mVfZmV4WVXO<s%IIW-=2b?GK)3fZgY3$3VkT^M6o zfB34(y}8CFrPG$5kvs~Fm>r$br&jyjT`QpJy*Z}4!(YQa?6mhI$!o$tTF;(W-p1v; z?USIMxAH2N3F(3=;!f|>j>YO;h>|(<gl)#EU6bbg66Z0D>exB|z;Z!lZ)X-=B?FtF z$mn{;0B?4VMDcF7WF`g%A65p20B=Sn5e9IYge1zzdOS`Fpj3&hN|ON@Br;6?$D=F4 R$_5f-0>ZgKTACTe0|4P2K@R`` diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/password-reset-api.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/password-reset-api.py index 486988df762..25067b67836 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/password-reset-api.py +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/password-reset-api.py @@ -54,7 +54,7 @@ def sendResetPasswordEmail(tr, userId, baseUrl): sendResetPasswordEmailInternal(tr, emailAddress, userId, token, baseUrl) def resetPassword(tr, userId, token): - if tokenIsValid(userId, token): + if tokenIsValid(tr, userId, token): email = getUserEmail(tr, userId) resetPasswordInternal(tr, email, userId) PersistentKeyValueStore.remove(userId + RESET_TOKEN_KEY_POSTFIX) @@ -78,8 +78,17 @@ def getJsonForData(data): jsonValue = objectMapper.writeValueAsString(data); return jsonValue; -def tokenIsValid(userId, token): - return PersistentKeyValueStore.get(userId + RESET_TOKEN_KEY_POSTFIX) == token +def tokenIsValid(tr, userId, token): + tokenAndTimestamp = PersistentKeyValueStore.get(userId + RESET_TOKEN_KEY_POSTFIX) + if (tokenAndTimestamp != None and tokenAndTimestamp["token"] == token): + timestampNow = time.time() + deltaInSeconds = timestampNow - float(tokenAndTimestamp["timestamp"]) + maxDelayInMinutes = float(getProperty(tr, "max-delay-in-minutes")) + if deltaInSeconds < maxDelayInMinutes * 60: + return True + else: + PersistentKeyValueStore.remove(userId + RESET_TOKEN_KEY_POSTFIX) + return False def sendResetPasswordEmailInternal(tr, email, userId, token, baseUrl): passwordResetLink = getPasswordResetLink(email, userId, token, baseUrl) diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/plugin.properties b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/plugin.properties index 18c916fc9a5..e8a94ee542c 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/plugin.properties +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/password-reset-api/plugin.properties @@ -1,6 +1,7 @@ label = Password Reset API class = ch.systemsx.cisd.openbis.dss.generic.server.plugins.jython.JythonIngestionService script-path = password-reset-api.py +max-delay-in-minutes = 2880 password-reset-request-subject = ELN-LIMS password reset for %s password-reset-request-body = Hi,\n\nA request has been made to reset the password of user %s.\n\nClick on this link in order to get a new password:\n%s\n\nSincere regards,\nYour ELN-LIMS Team new-password-subject = ELN-LIMS new password for %s -- GitLab