org.openestate.is24.restapi.webapp.VerificationServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.openestate.is24.restapi.webapp.VerificationServlet.java

Source

/*
 * Copyright 2014-2016 OpenEstate.org.
 *
 * Licensed 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 org.openestate.is24.restapi.webapp;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import oauth.signpost.exception.OAuthException;
import org.apache.commons.io.IOExceptionWithCause;
import org.apache.commons.lang3.StringUtils;
import org.openestate.is24.restapi.AbstractClient;
import org.openestate.is24.restapi.DefaultClient;
import org.openestate.is24.restapi.utils.Authorization;
import org.openestate.is24.restapi.utils.SslUtils;
import org.openestate.is24.restapi.utils.Verification;

/**
 * A servlet for verification at the REST-Webservice of ImmobilienScout24.de.
 *
 * @author Andreas Rudolph <andy@openindex.de>
 */
public class VerificationServlet extends HttpServlet {
    private final static long serialVersionUID = 4320407955026600846L;

    /**
     * A mapping that holds currently started verifications in memory.
     * <p>
     * By default the created verification credentials are stored in memory. You
     * should replace this method in productive systems - e.g. with a database
     * backend.
     */
    private final VerificationMap verifications = new VerificationMap();

    /**
     * Holds the webservice URL, that is configured in 'web.xml'.
     */
    private String webserviceUrl = null;

    /**
     * Holds the consumer key, that is configured in 'web.xml'.
     */
    private String consumerKey = null;

    /**
     * Holds the consumer secret, that is configured in 'web.xml'.
     */
    private String consumerSecret = null;

    /**
     * A GET request was received for this servlet.
     * <p>
     * The functions decides according to the request parameters, if a
     * verification is requested or if the response of a verification is
     * processed.
     *
     * @param req incoming GET request
     * @param resp outgoing response
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
        String state = StringUtils.trimToNull(req.getParameter("state"));
        String token = StringUtils.trimToNull(req.getParameter("oauth_token"));
        String verifier = StringUtils.trimToNull(req.getParameter("oauth_verifier"));
        if (state == null && token == null && verifier == null) {
            doVerificationRequest(req, resp);
        } else {
            doVerificationResponse(state, token, verifier, req, resp);
        }
    }

    /**
     * Create a verification request and show a page with the verification link.
     *
     * @param req incoming GET request
     * @param resp outgoing response
     * @throws IOException
     * @throws ServletException
     */
    protected void doVerificationRequest(HttpServletRequest req, HttpServletResponse resp)
            throws IOException, ServletException {
        Verification verification = null;
        try {
            verification = this.getWebserviceVerification(req);
        } catch (OAuthException ex) {
            throw new IOExceptionWithCause("Can't fetch verification!", ex);
        }

        // store verification informations
        this.storeVerification(verification);

        // put verification informations into request attributes
        req.setAttribute("url", verification.verificationUrl);
        req.setAttribute("token", verification.requestToken);
        req.setAttribute("secret", verification.requestTokenSecret);

        // show JSP view
        resp.setCharacterEncoding("UTF-8");
        RequestDispatcher d = req.getRequestDispatcher("/WEB-INF/views/verification-request.jsp");
        d.include(req, resp);
    }

    /**
     * Creates access credentials after the verification was passed.
     * <p>
     * The generated access token and secret is shown to the user by default.
     *
     * @param state the state of verification, that was received as request
     *        parameter by the webservice
     *
     * @param token the verification token, that was received as request parameter
     *        by the webservice
     *
     * @param verifier the verification code, that was received as request
     *        parameter by the webservice
     *
     * @param req incoming GET request
     * @param resp outgoing response
     * @throws IOException
     * @throws ServletException
     */
    protected void doVerificationResponse(String state, String token, String verifier, HttpServletRequest req,
            HttpServletResponse resp) throws IOException, ServletException {
        try {
            if (!"authorized".equalsIgnoreCase(state)) {
                req.setAttribute("authorized", false);
            } else {
                req.setAttribute("authorized", true);

                String secret = this.fetchVerificationSecret(token);
                if (token == null || secret == null) {
                    req.setAttribute("valid", false);
                } else {
                    Authorization authorization = null;
                    try {
                        authorization = getWebserviceAuthorization(token, secret, verifier, req);
                    } catch (OAuthException ex) {
                        throw new IOExceptionWithCause("Can't fetch authorization!", ex);
                    }
                    req.setAttribute("valid", true);
                    req.setAttribute("token", authorization.accessToken);
                    req.setAttribute("secret", authorization.accessTokenSecret);
                }
            }

            // show JSP view
            resp.setCharacterEncoding("UTF-8");
            RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/views/verification-response.jsp");
            dispatcher.include(req, resp);
        } finally {
            this.removeVerificationSecret(token);
        }
    }

    /**
     * Returns the secret for a previously created verification token.
     * <p>
     * By default the created verification credentials are stored in memory. You
     * should replace this method in productive systems - e.g. with a database
     * backend.
     *
     * @param token verification token
     * @return verification secret
     */
    protected String fetchVerificationSecret(String token) {
        // keep verification secret from local memory
        return this.verifications.get(token);
    }

    /**
     * Create a client for communication with the IS24-Webservice.
     *
     * @return client
     */
    protected AbstractClient getClient() {
        return new DefaultClient(this.webserviceUrl, this.consumerKey, this.consumerSecret);
    }

    /**
     * Generates credentials for permanent authoriation at the IS24-Webservice.
     *
     * @param token verification token
     * @param secret verification secret
     * @param verifier verification code
     * @param req incoming GET request
     * @return credentials for authorization
     * @throws OAuthException
     */
    protected Authorization getWebserviceAuthorization(String token, String secret, String verifier,
            HttpServletRequest req) throws OAuthException {
        final AbstractClient client = this.getClient();
        return client.authorizeAfterVerification(token, secret, verifier);
    }

    /**
     * Generates credentials for a verification process at the IS24-Webservice.
     *
     * @param req incoming GET request
     * @return credentials for verification
     * @throws OAuthException
     */
    protected Verification getWebserviceVerification(HttpServletRequest req) throws OAuthException {
        final AbstractClient client = this.getClient();
        return client.fetchVerification(req.getRequestURL().toString());
    }

    /**
     * Servlet is initialized by the servlet engine.
     *
     * @throws ServletException
     */
    @Override
    public void init() throws ServletException {
        super.init();
        final ServletContext context = this.getServletContext();
        List<String> errors = new ArrayList<String>();

        this.webserviceUrl = StringUtils.trimToNull(context.getInitParameter("WebserviceUrl"));
        if (this.webserviceUrl == null) {
            errors.add("No 'WebserviceUrl' is configured.");
        }

        this.consumerKey = StringUtils.trimToNull(context.getInitParameter("ConsumerKey"));
        if (this.consumerKey == null) {
            errors.add("No 'ConsumerKey' is configured.");
        }

        this.consumerSecret = StringUtils.trimToNull(context.getInitParameter("ConsumerSecret"));
        if (this.consumerSecret == null) {
            errors.add("No 'ConsumerSecret' is configured.");
        }

        if (!errors.isEmpty()) {
            throw new ServletException("Can't init servlet: " + StringUtils.join(errors, ", "));
        }

        // trust all SSL certificates / disable host name verification
        String trustAllCertificates = StringUtils.trimToNull(context.getInitParameter("TrustAllCertificates"));
        if ("1".equals(trustAllCertificates) || "true".equalsIgnoreCase(trustAllCertificates)) {
            try {
                // install all-trusting trust manager
                SslUtils.disableCertificateChecks();

                // install all-trusting host verifier
                SslUtils.disableHostnameVerification();
            } catch (Exception ex) {
                throw new ServletException("Can't setup insecure SSL context!", ex);
            }
        }
    }

    /**
     * Remove a previously created verification token from the local store.
     * <p>
     * By default the created verification credentials are stored in memory. You
     * should replace this method in productive systems - e.g. with a database
     * backend.
     *
     * @param token verification token
     */
    protected void removeVerificationSecret(String token) {
        // remove verification secret from local memory
        this.verifications.remove(token);
    }

    /**
     * Store newly created verification credentials to the local store.
     * <p>
     * By default the created verification credentials are stored in memory. You
     * should replace this method in productive systems - e.g. with a database
     * backend.
     *
     * @param verification verification credentials
     */
    protected void storeVerification(Verification verification) {
        // keep verification informations in local memory
        this.verifications.put(verification);
    }

    /**
     * A local store, that holds the last 50 created verification credentials.
     * <p>
     * By default the created verification credentials are stored in memory. You
     * should replace this method in productive systems - e.g. with a database
     * backend.
     */
    private final static class VerificationMap extends LinkedHashMap<String, String> {
        private final static long serialVersionUID = -8758568863108598387L;
        private final static int MAX_ENTRIES = 50;

        public void put(Verification verification) {
            this.put(verification.requestToken, verification.requestTokenSecret);
        }

        @Override
        protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
            return this.size() > MAX_ENTRIES;
        }
    }
}