This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository pollen. See https://gitlab.nuiton.org/chorem/pollen.git commit eb65c6382a3a215f8bee9b5f78b746273aad4d1b Author: Sylvain Bavencoff <bavencoff@codelutin.com> Date: Wed Jun 13 14:12:35 2018 +0200 refs #173 : Validation des emails sans token en base --- docs/config.md | 2 +- .../entity/PollenUserEmailAddressTopiaDao.java | 11 ----- .../pollen/persistence/entity/PollenUserImpl.java | 2 +- .../h2/V3_2_0_4__use_JWT_for_validate_email.sql | 19 ++++++++ .../V3_2_0_4__use_JWT_for_validate_email.sql | 19 ++++++++ pollen-persistence/src/main/xmi/pollen.properties | 2 +- pollen-persistence/src/main/xmi/pollen.zargo | Bin 31056 -> 30935 bytes pollen-services/src/main/config/PollenServices.ini | 2 +- .../services/config/PollenServicesConfig.java | 8 ++- .../pollen/services/service/CryptoService.java | 45 +++++++++++++++-- .../services/service/NotificationService.java | 12 ++--- .../pollen/services/service/PollenUserService.java | 38 +++++++-------- .../pollen/services/service/mail/EmailService.java | 13 +++-- .../services/service/security/SecurityService.java | 54 ++++++++++++++++++--- .../i18n/pollen-services_en_GB.properties | 2 +- .../i18n/pollen-services_fr_FR.properties | 2 +- 16 files changed, 169 insertions(+), 62 deletions(-) diff --git a/docs/config.md b/docs/config.md index 52b37094..460001b1 100644 --- a/docs/config.md +++ b/docs/config.md @@ -78,7 +78,7 @@ Si le mail reçu provient d’un retour d’une invitation à un sondage, il ser | Clés | Descriptions | Types | Valeurs par défaut | |---------------------------------------------------|-------------------------------------------------------------------------------------------------------|-----------------------------------------------|-----------------------| | pollen.data.directory | Répertoire de données de l’application | Alpha-numérique | | -| pollen.token.secret | Clés de chiffrement des jetons d'authentification | Alpha-numérique | !secret# | +| pollen.token.secret | Clés de chiffrement des jetons d'authentification (encodé en base 64)(16, 24 ou 32 bytes) | Alpha-numérique | IFYD6KLx/GrLZwSFBH5arXMIamWSA8qiVtFImIimDZs= | | pollen.token.issue | Le nom de l'émetteur des jetons d'authentification | Alpha-numérique | Pollen | | pollen.token.timeout | Durée de validité d'un jeton d'authentification (en secondes) | Numérique | 3600 | | pollen.default.pollType | Type de sondage par défaut lors de la création d'un nouveau sondage | FREE, RESTRICTED | FREE | diff --git a/pollen-persistence/src/main/java/org/chorem/pollen/persistence/entity/PollenUserEmailAddressTopiaDao.java b/pollen-persistence/src/main/java/org/chorem/pollen/persistence/entity/PollenUserEmailAddressTopiaDao.java index 968be61a..988b2db5 100644 --- a/pollen-persistence/src/main/java/org/chorem/pollen/persistence/entity/PollenUserEmailAddressTopiaDao.java +++ b/pollen-persistence/src/main/java/org/chorem/pollen/persistence/entity/PollenUserEmailAddressTopiaDao.java @@ -21,22 +21,11 @@ package org.chorem.pollen.persistence.entity; * #L% */ -import java.util.HashMap; -import java.util.Map; - public class PollenUserEmailAddressTopiaDao extends AbstractPollenUserEmailAddressTopiaDao<PollenUserEmailAddress> { public boolean emailExists(String email) { return forEmailAddressEquals(email).exists(); } - public PollenUserEmailAddress findEmailAddressByToken(String token) { - String hql = newFromClause() + - " WHERE " + PollenUserEmailAddress.PROPERTY_ACTIVATION_TOKEN + "." + PollenToken.PROPERTY_TOKEN + - " = :token"; - Map<String, Object> params = new HashMap<>(); - params.put("token", token); - return findUniqueOrNull(hql, params); - } } //PollenUserEmailAddressTopiaDao diff --git a/pollen-persistence/src/main/java/org/chorem/pollen/persistence/entity/PollenUserImpl.java b/pollen-persistence/src/main/java/org/chorem/pollen/persistence/entity/PollenUserImpl.java index bd84b16b..2ae14757 100644 --- a/pollen-persistence/src/main/java/org/chorem/pollen/persistence/entity/PollenUserImpl.java +++ b/pollen-persistence/src/main/java/org/chorem/pollen/persistence/entity/PollenUserImpl.java @@ -28,6 +28,6 @@ public class PollenUserImpl extends PollenUserAbstract { @Override public boolean isEmailValidated() { - return getDefaultEmailAddress() == null || getDefaultEmailAddress().getActivationToken() == null; + return getDefaultEmailAddress() == null || getDefaultEmailAddress().isValidated(); } } diff --git a/pollen-persistence/src/main/resources/db/migration/h2/V3_2_0_4__use_JWT_for_validate_email.sql b/pollen-persistence/src/main/resources/db/migration/h2/V3_2_0_4__use_JWT_for_validate_email.sql new file mode 100644 index 00000000..69431683 --- /dev/null +++ b/pollen-persistence/src/main/resources/db/migration/h2/V3_2_0_4__use_JWT_for_validate_email.sql @@ -0,0 +1,19 @@ +-- use JWT for validate email + +-- add validate field +alter table pollenUserEmailaddress add validated boolean; +update pollenUserEmailaddress set validated = activationtoken is null; + +-- drop activationtoken field +alter table pollenUserEmailaddress drop activationtoken; + +-- delete orphan token +delete from pollentoken where topiaid in ( + select t.topiaid from pollentoken t + left outer join pollenprincipal p + on p.permission = t.topiaid + left outer join sessiontoken s + on s.pollentoken = t.topiaid + where p.topiaid is null + and s.topiaid is null + ); \ No newline at end of file diff --git a/pollen-persistence/src/main/resources/db/migration/postgresql/V3_2_0_4__use_JWT_for_validate_email.sql b/pollen-persistence/src/main/resources/db/migration/postgresql/V3_2_0_4__use_JWT_for_validate_email.sql new file mode 100644 index 00000000..0de3ea85 --- /dev/null +++ b/pollen-persistence/src/main/resources/db/migration/postgresql/V3_2_0_4__use_JWT_for_validate_email.sql @@ -0,0 +1,19 @@ +-- use JWT for validate email + +-- add validate field +alter table pollenUserEmailaddress add validated boolean; +update pollenUserEmailaddress set validated = activationtoken is null; + +-- drop activationtoken field +alter table pollenUserEmailaddress drop activationtoken cascade; + +-- delete orphan token +delete from pollentoken where topiaid in ( + select t.topiaid from pollentoken t + left outer join pollenprincipal p + on p.permission = t.topiaid + left outer join sessiontoken s + on s.pollentoken = t.topiaid + where p.topiaid is null + and s.topiaid is null + ); \ No newline at end of file diff --git a/pollen-persistence/src/main/xmi/pollen.properties b/pollen-persistence/src/main/xmi/pollen.properties index ff910a8e..2c99e55e 100644 --- a/pollen-persistence/src/main/xmi/pollen.properties +++ b/pollen-persistence/src/main/xmi/pollen.properties @@ -18,7 +18,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # #L% ###m -model.tagvalue.version=3.2.0.3 +model.tagvalue.version=3.2.0.4 #model.tagValue.notGenerateToString=true #model.tagValue.constantPrefix=PROPERTY_ #model.tagValue.useEnumerationName=true diff --git a/pollen-persistence/src/main/xmi/pollen.zargo b/pollen-persistence/src/main/xmi/pollen.zargo index 419ff5bf..fee50cb2 100644 Binary files a/pollen-persistence/src/main/xmi/pollen.zargo and b/pollen-persistence/src/main/xmi/pollen.zargo differ diff --git a/pollen-services/src/main/config/PollenServices.ini b/pollen-services/src/main/config/PollenServices.ini index 71869494..a708830e 100644 --- a/pollen-services/src/main/config/PollenServices.ini +++ b/pollen-services/src/main/config/PollenServices.ini @@ -11,7 +11,7 @@ final = true description = pollen.configuration.token.secret key = pollen.token.secret type = string -defaultValue = !secret# +defaultValue = IFYD6KLx/GrLZwSFBH5arXMIamWSA8qiVtFImIimDZs= transient = true final = true diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/config/PollenServicesConfig.java b/pollen-services/src/main/java/org/chorem/pollen/services/config/PollenServicesConfig.java index 1f611143..b19988dc 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/config/PollenServicesConfig.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/config/PollenServicesConfig.java @@ -35,6 +35,7 @@ import org.chorem.pollen.services.bean.UsersRight; import org.nuiton.config.ApplicationConfig; import org.nuiton.config.ArgumentsParserException; +import java.util.Base64; import java.util.List; import java.util.Locale; import java.util.Map; @@ -171,6 +172,11 @@ public class PollenServicesConfig extends GeneratedPollenServicesConfig { public String getBuildDate() { return get().getOption(BUILD_DATE); - } + } + + public byte[] getTokenSecretBytes() { + String secret = getTokenSecret(); + return Base64.getDecoder().decode(secret); + } } diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/CryptoService.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/CryptoService.java index e60e0ec6..109f82f7 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/service/CryptoService.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/CryptoService.java @@ -21,11 +21,15 @@ import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodG import org.chorem.pollen.persistence.entity.PollenUserEmailAddress; import org.chorem.pollen.services.PollenTechnicalException; +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.security.GeneralSecurityException; import java.security.SecureRandom; import java.security.Security; import java.util.Date; @@ -40,6 +44,12 @@ public class CryptoService extends PollenServiceSupport { static final SecureRandom SECURE_RANDOM = new SecureRandom(); + static { + if (Security.getProvider(PROVIDER) == null) { + Security.addProvider(new BouncyCastleProvider()); + } + } + public PGPPublicKey getPublicKey(String email) { PGPPublicKey pgpPublicKey = null; @@ -119,10 +129,6 @@ public class CryptoService extends PollenServiceSupport { protected byte[] encryptMessage(byte[] message, PGPPublicKey pgpPublicKey) throws IOException, PGPException { - if (Security.getProvider(PROVIDER) == null) { - Security.addProvider(new BouncyCastleProvider()); - } - byte[] compressMessage = compressMessage(message); ByteArrayOutputStream bout = new ByteArrayOutputStream(); @@ -180,4 +186,35 @@ public class CryptoService extends PollenServiceSupport { } + public byte[] encryptMessageSymmetric(byte[] message) { + try { + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", PROVIDER); + cipher.init(Cipher.ENCRYPT_MODE, getSecretKey()); + return cipher.doFinal(message); + } catch (GeneralSecurityException e) { + throw new PollenTechnicalException("error on encrypt message", e); + } + } + + public byte[] decryptMessageSymmetric(byte[] encryptMessage) { + try { + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", PROVIDER); + cipher.init(Cipher.DECRYPT_MODE, getSecretKey()); + return cipher.doFinal(encryptMessage); + } catch (GeneralSecurityException e) { + throw new PollenTechnicalException("error on decrypt message", e); + } + } + + protected SecretKey getSecretKey() { + + byte[] keyBytes = getPollenServiceConfig().getTokenSecretBytes(); + + if (keyBytes.length != 16 && keyBytes.length != 24 && keyBytes.length != 32) { + throw new PollenTechnicalException("keyBytes wrong length for AES key"); + } + + return new SecretKeySpec(keyBytes, "AES"); + } + } diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/NotificationService.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/NotificationService.java index 8b576a06..ff31104d 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/service/NotificationService.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/NotificationService.java @@ -77,10 +77,10 @@ import java.util.Set; */ public class NotificationService extends PollenServiceSupport { - public void onUserCreated(PollenUser user) { + public void onUserCreated(PollenUser user, String token) { if (user.getDefaultEmailAddress() != null) { EmailService emailService = getEmailService(); - UserAccountCreatedEmail email = emailService.newUserAccountCreatedEmail(user); + UserAccountCreatedEmail email = emailService.newUserAccountCreatedEmail(user, token); email.addTo(user.getDefaultEmailAddress().getEmailAddress()); emailService.send(email); commit(); @@ -97,9 +97,9 @@ public class NotificationService extends PollenServiceSupport { } } - public void onResendValidation(PollenUser user, PollenUserEmailAddress emailAddress) { + public void onResendValidation(PollenUser user, PollenUserEmailAddress emailAddress, String token) { EmailService emailService = getEmailService(); - ResendValidationEmail email = emailService.newUserResendValidationEmail(user, emailAddress); + ResendValidationEmail email = emailService.newUserResendValidationEmail(user, token); email.addTo(emailAddress.getEmailAddress()); emailService.send(email); commit(); @@ -135,9 +135,9 @@ public class NotificationService extends PollenServiceSupport { } } - public void onUserEmailAddressAdded(PollenUser user, PollenUserEmailAddress emailAddress) { + public void onUserEmailAddressAdded(PollenUser user, PollenUserEmailAddress emailAddress, String token) { EmailService emailService = getEmailService(); - UserAccountEmailAddressAddedEmail email = emailService.newUserAccountEmailAddressAddedEmail(user, emailAddress); + UserAccountEmailAddressAddedEmail email = emailService.newUserAccountEmailAddressAddedEmail(user, token); email.addTo(emailAddress.getEmailAddress()); emailService.send(email); commit(); diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/PollenUserService.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/PollenUserService.java index e6d4dabb..942fc24c 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/service/PollenUserService.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/PollenUserService.java @@ -28,7 +28,6 @@ import org.apache.commons.logging.LogFactory; import org.chorem.pollen.persistence.entity.Comment; import org.chorem.pollen.persistence.entity.PollenPrincipal; import org.chorem.pollen.persistence.entity.PollenResource; -import org.chorem.pollen.persistence.entity.PollenToken; import org.chorem.pollen.persistence.entity.PollenUser; import org.chorem.pollen.persistence.entity.PollenUserEmailAddress; import org.chorem.pollen.persistence.entity.ResourceType; @@ -108,7 +107,7 @@ public class PollenUserService extends PollenServiceSupport implements PollenSer PollenUserEmailAddressBean bean = new PollenUserEmailAddressBean(); bean.setEntityId(entity.getTopiaId()); bean.setEmailAddress(entity.getEmailAddress()); - bean.setValidated(entity.getActivationToken() == null); + bean.setValidated(entity.isValidated()); bean.setPgpPublicKey(entity.getPgpPublicKey()); return bean; } @@ -173,7 +172,9 @@ public class PollenUserService extends PollenServiceSupport implements PollenSer PollenUser result = savePollenUser(user); commit(); - getNotificationService().onUserCreated(result); + String token = getSecurityService().generateEmailToken(result.getDefaultEmailAddress()); + + getNotificationService().onUserCreated(result, token); return PollenEntityRef.of(result); @@ -253,13 +254,13 @@ public class PollenUserService extends PollenServiceSupport implements PollenSer checkNotNull(token); PollenUser user = getUser0(userId); - PollenUserEmailAddress emailAddress = getPollenUserEmailAddressDao().findEmailAddressByToken(token); + PollenUserEmailAddress emailAddress = getSecurityService().getEmailAdresseFromToken(token); if (emailAddress == null || !user.containsEmailAddresses(emailAddress)) { throw new PollenInvalidEmailActivationTokenException(); } // reset token in database - emailAddress.setActivationToken(null); + emailAddress.setValidated(true); commit(); @@ -280,7 +281,7 @@ public class PollenUserService extends PollenServiceSupport implements PollenSer } // reset token in database - emailAddress.setActivationToken(null); + emailAddress.setValidated(true); commit(); @@ -297,12 +298,12 @@ public class PollenUserService extends PollenServiceSupport implements PollenSer } PollenUser user = getPollenUserDao().forEmailAddressesContains(userEmailAddress).findUnique(); - if (userEmailAddress.getActivationToken() == null) { - userEmailAddress.setActivationToken(getSecurityService().generateNewToken()); - commit(); - } + userEmailAddress.setValidated(false); + commit(); - getNotificationService().onResendValidation(user, userEmailAddress); + String token = getSecurityService().generateEmailToken(userEmailAddress); + + getNotificationService().onResendValidation(user, userEmailAddress, token); } @@ -321,7 +322,7 @@ public class PollenUserService extends PollenServiceSupport implements PollenSer PollenUser admin = savePollenUser(adminBean); admin.setAdministrator(true); - admin.getDefaultEmailAddress().setActivationToken(null); + admin.getDefaultEmailAddress().setValidated(true); commit(); } @@ -376,12 +377,13 @@ public class PollenUserService extends PollenServiceSupport implements PollenSer PollenUserEmailAddress address = getPollenUserEmailAddressDao().create(); address.setEmailAddress(emailAddress); - PollenToken emailActivation = getSecurityService().generateNewToken(); - address.setActivationToken(emailActivation); + address.setValidated(false); user.addEmailAddresses(address); commit(); - getNotificationService().onUserEmailAddressAdded(user, address); + String token = getSecurityService().generateEmailToken(address); + + getNotificationService().onUserEmailAddressAdded(user, address, token); return PollenEntityRef.of(address); } @@ -411,7 +413,7 @@ public class PollenUserService extends PollenServiceSupport implements PollenSer checkNotNull(emailAddressId); PollenUserEmailAddress emailAddress = user.getEmailAddressesByTopiaId(emailAddressId); checkNotNull(emailAddress); - if (emailAddress.getActivationToken() != null) { + if (!emailAddress.isValidated()) { throw new PollenEmailNotValidatedException(); } user.setDefaultEmailAddress(emailAddress); @@ -478,8 +480,6 @@ public class PollenUserService extends PollenServiceSupport implements PollenSer } else { - PollenToken emailActivation = getSecurityService().generateNewToken(); - toSave = getPollenUserDao().create(); PollenUserEmailAddressBean emailAddress = user.getDefaultEmailAddress(); @@ -487,7 +487,7 @@ public class PollenUserService extends PollenServiceSupport implements PollenSer PollenUserEmailAddress defaultEmailAddress = getPollenUserEmailAddressDao().create(); String cleanMail = getCleanMail(emailAddress.getEmailAddress()); defaultEmailAddress.setEmailAddress(cleanMail); - defaultEmailAddress.setActivationToken(emailActivation); + defaultEmailAddress.setValidated(false); toSave.addEmailAddresses(defaultEmailAddress); toSave.setDefaultEmailAddress(defaultEmailAddress); } diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/mail/EmailService.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/mail/EmailService.java index 541cbb4a..a92d54a5 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/service/mail/EmailService.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/mail/EmailService.java @@ -45,7 +45,6 @@ import org.chorem.pollen.persistence.entity.Poll; import org.chorem.pollen.persistence.entity.PollenPrincipal; import org.chorem.pollen.persistence.entity.PollenResource; import org.chorem.pollen.persistence.entity.PollenUser; -import org.chorem.pollen.persistence.entity.PollenUserEmailAddress; import org.chorem.pollen.persistence.entity.Report; import org.chorem.pollen.persistence.entity.UserCredential; import org.chorem.pollen.persistence.entity.Vote; @@ -248,14 +247,14 @@ public class EmailService extends PollenServiceSupport { return email; } - public UserAccountCreatedEmail newUserAccountCreatedEmail(PollenUser user) { + public UserAccountCreatedEmail newUserAccountCreatedEmail(PollenUser user, String token) { UserAccountCreatedEmail email = new UserAccountCreatedEmail(getLocale(), getTimeZone()); email.setUser(user); PollenEntityId<PollenUser> userId = getPollenEntityId(user); email.setValidateUrl(getPollenUIUrlRenderService().getUserValidateUrl(getUIContext().getUserValidateUrl(), userId.getReducedId(), - user.getDefaultEmailAddress().getActivationToken().getToken())); + token)); return email; } @@ -282,13 +281,13 @@ public class EmailService extends PollenServiceSupport { } public UserAccountEmailAddressAddedEmail newUserAccountEmailAddressAddedEmail(PollenUser user, - PollenUserEmailAddress emailAddress) { + String token) { UserAccountEmailAddressAddedEmail email = new UserAccountEmailAddressAddedEmail(getLocale(), getTimeZone()); email.setUser(user); PollenEntityId<PollenUser> userId = getPollenEntityId(user); email.setValidateUrl(getPollenUIUrlRenderService().getUserValidateUrl(getUIContext().getUserValidateUrl(), userId.getReducedId(), - emailAddress.getActivationToken().getToken())); + token)); return email; } @@ -299,14 +298,14 @@ public class EmailService extends PollenServiceSupport { return email; } - public ResendValidationEmail newUserResendValidationEmail(PollenUser user, PollenUserEmailAddress emailAddress) { + public ResendValidationEmail newUserResendValidationEmail(PollenUser user, String token) { ResendValidationEmail email = new ResendValidationEmail(getLocale(), getTimeZone()); email.setUser(user); PollenEntityId<PollenUser> userId = getPollenEntityId(user); email.setValidateUrl(getPollenUIUrlRenderService().getUserValidateUrl(getUIContext().getUserValidateUrl(), userId.getReducedId(), - emailAddress.getActivationToken().getToken())); + token)); return email; } diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/security/SecurityService.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/security/SecurityService.java index 0a9903a9..58dc2de3 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/service/security/SecurityService.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/security/SecurityService.java @@ -27,6 +27,7 @@ import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTVerificationException; import com.auth0.jwt.interfaces.DecodedJWT; import com.google.common.base.Preconditions; +import com.google.common.primitives.Longs; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -48,14 +49,15 @@ import org.chorem.pollen.persistence.entity.VoteVisibility; import org.chorem.pollen.persistence.entity.VoterList; import org.chorem.pollen.persistence.entity.VoterListMember; import org.chorem.pollen.services.PollenServiceContext; -import org.chorem.pollen.services.PollenTechnicalException; import org.chorem.pollen.services.bean.PollenEntityRef; import org.chorem.pollen.services.bean.UsersRight; import org.chorem.pollen.services.service.PollService; import org.chorem.pollen.services.service.PollenServiceSupport; import org.nuiton.topia.persistence.TopiaNoResultException; -import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Base64; import java.util.Calendar; import java.util.Date; import java.util.Objects; @@ -249,12 +251,48 @@ public class SecurityService extends PollenServiceSupport { } protected Algorithm getAlgorithm() { - String secret = getPollenServiceConfig().getTokenSecret(); - try { - return Algorithm.HMAC256(secret); - } catch (UnsupportedEncodingException e) { - throw new PollenTechnicalException(e); + byte[] secret = getPollenServiceConfig().getTokenSecretBytes(); + return Algorithm.HMAC256(secret); + } + + public String generateEmailToken(PollenUserEmailAddress email) { + long expireDate = getNow().getTime() + getPollenServiceConfig().getTokenTimeout() * 1000; + byte[] bytesEmail = email.getEmailAddress().getBytes(); + + + ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES + bytesEmail.length); + buffer.putLong(expireDate); + buffer.put(bytesEmail); + byte[] message = buffer.array(); + + byte[] encryptMessage = getCryptoService().encryptMessageSymmetric(message); + + String token = Base64.getUrlEncoder().encodeToString(encryptMessage); + return token; + } + + public PollenUserEmailAddress getEmailAdresseFromToken(String token) { + PollenUserEmailAddress pollenUserEmailAddress = null; + + if (StringUtils.isNotBlank(token)) { + + byte[] encryptMessage = Base64.getUrlDecoder().decode(token); + + byte[] message = getCryptoService().decryptMessageSymmetric(encryptMessage); + + byte[] bytesExpireDate = Arrays.copyOfRange(message, 0, Long.BYTES); + byte[] bytesEmail = Arrays.copyOfRange(message, Long.BYTES, message.length); + + long expireDate = Longs.fromByteArray(bytesExpireDate); + String emailAdresse = new String(bytesEmail); + + if (getNow().getTime() <= expireDate) { + pollenUserEmailAddress = getPollenUserEmailAddressDao().forEmailAddressEquals(emailAdresse).findUniqueOrNull(); + + } + } + return pollenUserEmailAddress; } public PollenToken generateNewToken() { @@ -377,7 +415,7 @@ public class SecurityService extends PollenServiceSupport { protected boolean isConnectedUserAcceptEmailSuffix(String suffix) { return getConnectedUser().getEmailAddresses().stream() - .filter(email -> email.getActivationToken() == null) // email Validate + .filter(PollenUserEmailAddress::isValidated) // email Validate .map(PollenUserEmailAddress::getEmailAddress) .anyMatch(email -> email.endsWith(suffix)); } diff --git a/pollen-services/src/main/resources/i18n/pollen-services_en_GB.properties b/pollen-services/src/main/resources/i18n/pollen-services_en_GB.properties index 28e65d98..dc059689 100644 --- a/pollen-services/src/main/resources/i18n/pollen-services_en_GB.properties +++ b/pollen-services/src/main/resources/i18n/pollen-services_en_GB.properties @@ -32,7 +32,7 @@ pollen.configuration.smtpFrom=Smtp From pollen.configuration.smtpPort=Smtp Port pollen.configuration.smtpWait=Time between two send mail to smtp pollen.configuration.token.issue=Producer name for authentification tokens -pollen.configuration.token.secret=secret key for authentification tokens +pollen.configuration.token.secret=secret key for authentification tokens (encoding in base 64) (16, 24 or 32 bytes) pollen.configuration.token.timeout=Inactivity delay before invalidate the session of a user (in seconds) pollen.configuration.userConnectedRequired=Only connected users can be access on application pollen.configuration.usersCanCreatePoll=Wich user can create Poll ("All_USERS", "USERS_CONNECTED" or "USERS_SELECTED") diff --git a/pollen-services/src/main/resources/i18n/pollen-services_fr_FR.properties b/pollen-services/src/main/resources/i18n/pollen-services_fr_FR.properties index 6407eb33..95a8f648 100644 --- a/pollen-services/src/main/resources/i18n/pollen-services_fr_FR.properties +++ b/pollen-services/src/main/resources/i18n/pollen-services_fr_FR.properties @@ -32,7 +32,7 @@ pollen.configuration.smtpFrom=Expéditeur pollen.configuration.smtpPort=Port smtp pollen.configuration.smtpWait=Intervalle de temps entre deux envois de mail au SMTP pollen.configuration.token.issue=Nom du producteur de jeton d'authentification -pollen.configuration.token.secret=Clé secret pour chiffer le jetons d'authnetification +pollen.configuration.token.secret=Clé secret pour chiffer le jetons d'authnetification (encodé en base 64) (16, 24 ou 32 bytes) pollen.configuration.token.timeout=Temps autorisé d'inactivité avant d'invalider une session utilisateur (en secondes) pollen.configuration.userConnectedRequired=Seul les utilisateurs connectés peuvent accéder à l'application pollen.configuration.usersCanCreatePoll=Quels utilisateurs peuvent créer des sondages ("All_USERS", "USERS_CONNECTED" ou "USERS_SELECTED") -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.