001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.camel.component.gae.login;
018    
019    import java.io.OutputStreamWriter;
020    import java.io.PrintWriter;
021    import java.net.HttpURLConnection;
022    import java.net.URL;
023    import java.net.URLEncoder;
024    import java.nio.charset.Charset;
025    
026    import com.google.gdata.client.GoogleAuthTokenFactory;
027    import com.google.gdata.util.AuthenticationException;
028    
029    /**
030     * Implements the interactions with Google's authentication and authorization
031     * services. If the endpoint is configured to run in development mode the
032     * authentication and authorization services of the development server are used.
033     */
034    public class GLoginServiceImpl implements GLoginService {
035    
036        /**
037         * Authenticates a user and stores the authentication token to
038         * {@link GLoginData#setAuthenticationToken(String)}. If the endpoint is
039         * configured to run in development mode this method simply returns without
040         * any further action. 
041         */
042        public void authenticate(GLoginData data) throws AuthenticationException {
043            if (data.isDevMode()) {
044                return;
045            }
046            GoogleAuthTokenFactory factory = 
047                new GoogleAuthTokenFactory("ah", data.getClientName(), null);
048            String token = factory.getAuthToken(
049                data.getUserName(), 
050                data.getPassword(), 
051                null, null, "ah", data.getClientName());
052            data.setAuthenticationToken(token);       
053        }
054    
055        /**
056         * Dispatches authorization to {@link #authorizeDev(GLoginData)} if the
057         * endpoint is configured to run in development mode, otherwise to
058         * {@link #authorizeStd(GLoginData)}.
059         */
060        public void authorize(GLoginData data) throws Exception {
061            if (data.isDevMode()) {
062                authorizeDev(data);
063            } else {
064                authorizeStd(data);
065            }
066        }
067    
068        /**
069         * Authorizes access to a development server and stores the resulting
070         * authorization cookie to {@link GLoginData#setAuthorizationCookie(String)}
071         * . Authorization in development mode doesn't require an authentication
072         * token.
073         */
074        protected void authorizeDev(GLoginData data) throws Exception {
075            String homeLocation = String.format("http://%s:%d", data.getHostName(), data.getDevPort());
076            HttpURLConnection connection = createURLConnection(homeLocation + "/_ah/login", true);
077            connection.connect();
078            PrintWriter writer = new PrintWriter(
079                new OutputStreamWriter(connection.getOutputStream()));
080            writer.println(String.format("email=%s&isAdmin=%s&continue=%s",
081                URLEncoder.encode(data.getUserName(), Charset.defaultCharset().name()), 
082                data.isDevAdmin() ? "on" : "off", 
083                URLEncoder.encode(homeLocation, Charset.defaultCharset().name())));
084            writer.flush();
085            data.setAuthorizationCookie(connection.getHeaderField("Set-Cookie"));
086            connection.disconnect();        
087        }
088        
089        /**
090         * Authorizes access to a Google App Engine application and stores the
091         * resulting authorization cookie to
092         * {@link GLoginData#setAuthorizationCookie(String)}. This method requires
093         * an authentication token from
094         * {@link GLoginData#getAuthenticationToken()}.
095         */
096        protected void authorizeStd(GLoginData data) throws Exception {
097            String url = String.format("https://%s/_ah/login?auth=%s",
098                data.getHostName(), data.getAuthenticationToken());
099            HttpURLConnection connection = createURLConnection(url, false);
100            connection.connect();
101            data.setAuthorizationCookie(connection.getHeaderField("Set-Cookie"));
102            connection.disconnect();
103        }
104        
105        private static HttpURLConnection createURLConnection(String url, boolean dev) throws Exception {
106            // TODO: support usage of proxy (via endpoint parameters or GLoginData object)
107            HttpURLConnection connection = (HttpURLConnection)new URL(url).openConnection();
108            connection.setInstanceFollowRedirects(false);
109            if (dev) {
110                connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
111                connection.setRequestMethod("POST");
112                connection.setDoOutput(true);
113            }
114            return connection;
115        }
116        
117    }