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}