001/* 002 * Stallion Core: A Modern Web Framework 003 * 004 * Copyright (C) 2015 - 2016 Stallion Software LLC. 005 * 006 * This program is free software: you can redistribute it and/or modify it under the terms of the 007 * GNU General Public License as published by the Free Software Foundation, either version 2 of 008 * the License, or (at your option) any later version. This program is distributed in the hope that 009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 011 * License for more details. You should have received a copy of the GNU General Public License 012 * along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>. 013 * 014 * 015 * 016 */ 017 018package io.stallion.services; 019 020import io.stallion.Context; 021import io.stallion.dataAccess.db.DB; 022import io.stallion.exceptions.ClientException; 023import io.stallion.utils.DateUtils; 024import org.apache.commons.lang3.StringUtils; 025 026import java.math.BigInteger; 027import java.security.SecureRandom; 028import java.time.ZonedDateTime; 029import java.util.Base64; 030 031 032public class SecureTempTokens { 033 public static TempToken getOrCreate(String key) throws Exception { 034 ZonedDateTime expires = DateUtils.utcNow(); 035 expires = expires.plusDays(7); 036 return getOrCreate(key, expires); 037 } 038 public static TempToken getOrCreate(String key, ZonedDateTime expiresAt) throws Exception { 039 TempToken token = (TempToken) DB.instance().fetchOne(TempToken.class, "customkey", key); 040 ZonedDateTime now = DateUtils.utcNow(); 041 // If the token has already been used or is expired, we generate a new token 042 if (token != null && token.getUsedAt() == null && now.isBefore(token.getExpiresAt())) { 043 return token; 044 } 045 if (token == null) { 046 token = new TempToken(); 047 token.setId(Context.dal().getTickets().nextId()); 048 } 049 token.setCreatedAt(now); 050 token.setExpiresAt(expiresAt); 051 token.setCustomKey(key); 052 token.setToken(idToRandomString((Long)token.getId())); 053 token.setUsedAt(null); 054 DB.instance().save(token); 055 return token; 056 } 057 058 public static TempToken markUsed(TempToken token) throws Exception { 059 token.setUsedAt(DateUtils.utcNow()); 060 DB.instance().save(token); 061 return token; 062 } 063 064 public static TempToken fetchToken(String tokenString) throws Exception { 065 if (StringUtils.isBlank(tokenString)) { 066 throw new ClientException("The passed in token is empty"); 067 } 068 TempToken token = (TempToken)DB.instance().fetchOne(TempToken.class, "token", tokenString); 069 if (token == null) { 070 throw new ClientException("Token not found"); 071 } 072 ZonedDateTime now = DateUtils.utcNow(); 073 if (now.compareTo(token.getExpiresAt()) > 0) { 074 throw new ClientException("Token has expired"); 075 } 076 if (token.getUsedAt() != null) { 077 throw new ClientException("Token was already used"); 078 } 079 return token; 080 } 081 082 public static String idToRandomString(Long id) { 083 SecureRandom random = new SecureRandom(); 084 Integer rand = random.nextInt(); 085 Long n = rand * 1000000000 + id; 086 return Base64.getEncoder().encodeToString(BigInteger.valueOf(n).toByteArray()); 087 } 088}