001/* 002 * Copyright 2014 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.core.captcha; 017 018import java.util.Collection; 019import java.util.regex.Pattern; 020 021import org.apache.avalon.framework.activity.Disposable; 022import org.apache.avalon.framework.activity.Initializable; 023import org.apache.avalon.framework.service.ServiceException; 024import org.apache.avalon.framework.service.ServiceManager; 025import org.apache.avalon.framework.service.Serviceable; 026 027import org.ametys.runtime.config.Config; 028 029/** 030 * Helper for generating a captcha regardless of its implementation 031 */ 032public final class CaptchaHelper implements Serviceable, Initializable, Disposable 033{ 034 /** The avalon role */ 035 public static final String ROLE = CaptchaHelper.class.getName(); 036 037 private static final String _CAPTCHA_TYPE_KEY = "runtime.captcha.type"; 038 private static final String _DEFAULT_CAPTCHA_IMPL = "jcaptcha"; 039 040 private static CaptchaExtensionPoint _captchaExtensionPoint; 041 042 private static Captcha _captcha; 043 044 public void initialize() throws Exception 045 { 046 _captcha = null; 047 } 048 049 public void service(ServiceManager manager) throws ServiceException 050 { 051 _captchaExtensionPoint = (CaptchaExtensionPoint) manager.lookup(CaptchaExtensionPoint.ROLE); 052 } 053 054 /** 055 * Retrieve the type of captcha used 056 * @return The type of captcha. 057 */ 058 public static String getCaptchaType() 059 { 060 if (_captcha != null) 061 { 062 return _captcha.getId(); 063 } 064 else if (Config.getInstance() != null && _captchaExtensionPoint != null && _captchaExtensionPoint.hasExtension(Config.getInstance().getValue(_CAPTCHA_TYPE_KEY))) 065 { 066 return Config.getInstance().getValue(_CAPTCHA_TYPE_KEY); 067 } 068 else 069 { 070 return _DEFAULT_CAPTCHA_IMPL; 071 } 072 } 073 074 /** 075 * Retrieve the used captcha 076 * @return The captcha. 077 */ 078 public static Captcha getCaptcha() 079 { 080 if (_captcha != null) 081 { 082 return _captcha; 083 } 084 else if (_captchaExtensionPoint != null) 085 { 086 String key = Config.getInstance() != null ? Config.getInstance().getValue(_CAPTCHA_TYPE_KEY) : null; 087 // In safe mod or if configured captcha does not exists, use default impl 088 if (key == null || !_captchaExtensionPoint.hasExtension(key)) 089 { 090 key = _DEFAULT_CAPTCHA_IMPL; 091 } 092 _captcha = _captchaExtensionPoint.getExtension(key); 093 return _captcha; 094 } 095 else 096 { 097 throw new IllegalStateException("No captcha implementation found"); 098 } 099 } 100 101 /** 102 * Check a captcha 103 * @param key The captcha key. Can be empty or null when using reCaptcha. 104 * @param value The value to check 105 * @return <code>true</code> if the captcha is valid, false otherwise. 106 */ 107 public static boolean checkAndInvalidate(String key, String value) 108 { 109 return getCaptcha().checkAndInvalidateCaptcha(key, value); 110 } 111 112 /** 113 * Get the i18n key to use for the message to display when captcha verification has failed 114 * @return The i18n key. 115 */ 116 public static String getFailedI18NKey() 117 { 118 return getCaptcha().getLoginFailedBecauseCaptchaFailedLabel().getKey(); 119 } 120 121 /** 122 * Get the catalog to use for the message to display when captcha verification has failed 123 * @return The catalog. 124 */ 125 public static String getFailedI18NCatalog() 126 { 127 return getCaptcha().getLoginFailedBecauseCaptchaFailedLabel().getCatalogue(); 128 } 129 130 /** 131 * Get the i18n key to use for the message to display when too many connection attempts have been made 132 * @return The key. 133 */ 134 public static String getTooManyAttemptI18NKey() 135 { 136 return getCaptcha().getLoginFailedBecauseCaptchaFailedLabel().getKey(); 137 } 138 139 /** 140 * Get the catalog to use for the message to display when too many connection attempts have been made 141 * @return The catalog. 142 */ 143 public static String getTooManyAttemptI18NCatalog() 144 { 145 return getCaptcha().getLoginFailedBecauseCaptchaFailedLabel().getCatalogue(); 146 } 147 148 /** 149 * Get the minimal url patterns used by the captcha implementation. 150 * This is necessary to grant those url even when the user is not authentified (in particular to set a catpcha on the login page). 151 * Do no put a too wide range (such as ^.*$) since it may introduce security issues. 152 * @return The minimal url patterns used by the captcha implementation. 153 */ 154 public static Collection<Pattern> getUsedUrlPatterns() 155 { 156 return getCaptcha().getUsedUrlPatterns(); 157 } 158 159 /** 160 * Determine if the captcha provider requires user interaction 161 * If not, no visible rendering should be done 162 * @return true if user interaction are needed 163 */ 164 public static boolean requiresUserInteraction() 165 { 166 return getCaptcha().requireUserInteraction(); 167 } 168 169 @Override 170 public void dispose() 171 { 172 staticDispose(); 173 } 174 175 /** 176 * Dispose the CaptchaHelper 177 */ 178 public static void staticDispose() 179 { 180 _captcha = null; 181 } 182}