/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 net.formio.security;
/**
* Implementation of {@link TokenAuthorizer} using hash-based tokens. Immutable.
* @author Radek Beran
*/
public class HashTokenAuthorizer extends AbstractTokenAuthorizer {
private static final String TOKEN_PART_SEPARATOR = "_";
@Override
public String generateToken(String secret) {
if (secret == null || secret.isEmpty()) throw new IllegalArgumentException("secret cannot be empty");
long time = System.currentTimeMillis();
String token = tokenFromSecretAndTime(secret, time);
return token;
}
@Override
public boolean isValidToken(String token, String secret) {
if (token == null || token.isEmpty()) return false;
if (secret == null || secret.isEmpty()) return false;
long tokenTime = getTimeFromToken(token);
String reconstructedToken = tokenFromSecretAndTime(secret, tokenTime);
if (!token.equals(reconstructedToken)) {
// input token was not constructed using the same secret
return false;
}
long currentTime = System.currentTimeMillis();
if (Math.abs(currentTime - tokenTime) > getMaxAllowedTimeDifference()) {
// validity of input token has expired
return false;
}
return true;
}
protected String getHashAlgorithm() {
return "SHA-256";
}
/**
* Returns maximum allowed difference between time of token generation
* and time of token validation in milliseconds.
* @return
*/
protected long getMaxAllowedTimeDifference() {
return 6 * 60 * 60 * 1000; // 6 h
}
String tokenFromSecretAndTime(String secret, long time) {
String payload = secret + time;
String token = SecurityUtils.hash(payload, getHashAlgorithm()) + TOKEN_PART_SEPARATOR + time;
return token;
}
private long getTimeFromToken(String token) {
long time = 0L;
if (token == null || token.isEmpty()) return time;
int idxOfSep = token.lastIndexOf(TOKEN_PART_SEPARATOR);
if (idxOfSep >= 0) {
String timeStr = token.substring(idxOfSep + TOKEN_PART_SEPARATOR.length());
if (timeStr != null && !timeStr.isEmpty()) {
try {
time = Long.valueOf(timeStr).longValue();
} catch (NumberFormatException ignored) {
// ignored
}
}
}
return time;
}
}