001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.ui.login; 029 030import org.opencms.file.CmsDataAccessException; 031import org.opencms.file.CmsObject; 032import org.opencms.file.CmsUser; 033import org.opencms.i18n.CmsEncoder; 034import org.opencms.main.CmsException; 035 036import java.util.Arrays; 037import java.util.List; 038 039import org.apache.commons.lang3.RandomStringUtils; 040 041/** 042 * Helper class for dealing with authorization tokens for the 'forgot password' functionality.<p> 043 * 044 * When a user requests a link to change his password, an authorization token is generated and also stored in the user's 045 * additional info (in a slightly different form). When the user opens the 'change password' link sent to him, the authentication 046 * token is validated by comparing it to the token in the user's additional info. Additionally, the system checks whether the age 047 * of the token stored in the additional infos is older than the maximum age, which can also be configured. 048 * 049 */ 050public class CmsTokenValidator { 051 052 /** Additional info key to store the authorization data. */ 053 public static final String ADDINFO_KEY = "RESET_AUTH"; 054 055 /** The user. */ 056 private CmsUser m_user; 057 058 /** 059 * Removes an authorization token from the user's additional information.<p> 060 * 061 * @param cms the CMS context 062 * @param user the user 063 * @throws CmsException if something goes wrong 064 */ 065 public static void clearToken(CmsObject cms, CmsUser user) throws CmsException { 066 067 user.getAdditionalInfo().remove(ADDINFO_KEY); 068 cms.writeUser(user); 069 } 070 071 /** 072 * Creates a new token for the given user and stores it in the user's additional info.<p> 073 * 074 * @param cms the CMS context 075 * @param user the user 076 * @param currentTime the current time 077 * @return the authorization token 078 * @throws CmsException if something goes wrong 079 */ 080 public static String createToken(CmsObject cms, CmsUser user, long currentTime) throws CmsException { 081 082 String randomKey = RandomStringUtils.randomAlphanumeric(8); 083 String value = CmsEncoder.encodeStringsAsBase64Parameter(Arrays.asList(randomKey, "" + currentTime)); 084 user.setAdditionalInfo(ADDINFO_KEY, value); 085 cms.writeUser(user); 086 return CmsEncoder.encodeStringsAsBase64Parameter(Arrays.asList(user.getName(), randomKey)); 087 } 088 089 /** 090 * Gets the user.<p> 091 * 092 * @return the user 093 */ 094 public CmsUser getUser() { 095 096 return m_user; 097 } 098 099 /** 100 * Validates the authentication token against the token stored in the user's additional info.<p> 101 * 102 * @param cms the CMS context 103 * @param token the authentication token 104 * @param maxAgeMillis the maximum token age in milliseconds 105 * 106 * @return null if the validation is succesfull, or a string containing the error message if not 107 * @throws CmsException if something goes wrong 108 */ 109 public String validateToken(CmsObject cms, String token, long maxAgeMillis) throws CmsException { 110 111 try { 112 List<String> tokenValues = CmsEncoder.decodeStringsFromBase64Parameter(token); 113 114 if (tokenValues.size() != 2) { 115 return "Invalid token"; 116 } 117 CmsUser user = cms.readUser(tokenValues.get(0)); 118 String userValue = (String)user.getAdditionalInfo(ADDINFO_KEY); 119 if (userValue == null) { 120 return "no additional infos found"; 121 } 122 List<String> userValues = CmsEncoder.decodeStringsFromBase64Parameter(userValue); 123 if (userValues.get(0).equals(tokenValues.get(1))) { 124 String strUserTime = userValues.get(1); 125 long usertime = Long.valueOf(strUserTime).longValue(); 126 if ((System.currentTimeMillis() - usertime) > maxAgeMillis) { 127 return "Auth token too old"; 128 } 129 m_user = user; 130 return null; 131 } else { 132 return "Key mismatch"; 133 } 134 } catch (IllegalArgumentException e) { 135 return "Token parse error"; 136 } catch (CmsDataAccessException e) { 137 return "User not found"; 138 } 139 } 140 141}