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}