/** * Copyright (C) 2009-2010 Wilfred Springer * Changed by Ernst Vorsteveld * * 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 nl.iwelcome.connector.google; import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.concurrent.atomic.AtomicReference; import nl.iwelcome.connector.google.domain.GoogleAtom; import org.springframework.http.HttpMethod; import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.converter.ByteArrayHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.web.client.RequestCallback; import org.springframework.web.client.ResponseExtractor; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; /** * An implementation of {@link nl.flotsam.greader.GoogleAppsOperations} managing * authentication for its clients. * * http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html#Request */ public class GoogleAppsTemplate implements GoogleAppsOperations { private final String email; private final String password; private final RestTemplate restTemplate; private final String loginUrl; private final String baseUrl; private final String customerIdUrl; private final String moveToGroupUrl; private final AtomicReference<String> auth = new AtomicReference<String>(); /** * Constructs a new instance. * * @param email * The login the account to manage. * @param password * The password. * @param tracing * A boolean indicating if tracing should be enabled. */ public GoogleAppsTemplate(String email, String password, String loginUrl, String baseUrl, String customerIdUrl, String moveToGroupUrl, boolean tracing) { this.loginUrl = loginUrl; this.baseUrl = baseUrl; this.customerIdUrl = customerIdUrl; this.moveToGroupUrl = moveToGroupUrl; this.password = password; this.email = email; restTemplate = new AuthenticatingRestTemplate(); List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>( restTemplate.getMessageConverters()); converters.add(new PropertiesHttpMessageConverter()); converters.add(new XmlMessageConverter()); converters.add(new StringHttpMessageConverter()); converters.add(new ByteArrayHttpMessageConverter()); restTemplate.setMessageConverters(converters); if (tracing) { restTemplate.setRequestFactory(new TracingClientHttpRequestFactory(restTemplate.getRequestFactory())); } } /** * Constructs a new instance. * * @param email * The email address of the account to manage. * @param password * The password. */ public GoogleAppsTemplate(String email, String password, String loginUrl, String baseUrl, String customerIdUrl, String moveToGroupUrl) { this(email, password, loginUrl, baseUrl, customerIdUrl, moveToGroupUrl, false); } @Override public <T> T doWithCallback(ReaderCallback<T> callback) { if (auth.get() == null) { updateAuth(); } return callback.execute(restTemplate); } @Override public String getToken() { return doWithCallback(new ReaderCallback<String>() { @Override public String execute(RestOperations operations) { return operations.getForObject(baseUrl, String.class); } }); } private void updateAuth() { String current = auth.get(); String replacement = authenticate(); auth.compareAndSet(current, replacement); } private String authenticate() { Properties result = RestInvoker.preparePostTo(loginUrl) .using(restTemplate) .expecting(Properties.class) .withParam("accountType", "HOSTED") .withParam("Email", email) .withParam("Passwd", password) .withParam("service", "apps") .execute(); return (String) result.get("Auth"); } private class AuthenticatingRestTemplate extends RestTemplate { @Override protected <T> T doExecute(URI url, HttpMethod method, final RequestCallback requestCallback, ResponseExtractor<T> responseExtractor) throws RestClientException { return super.doExecute(url, method, new RequestCallback() { @Override public void doWithRequest(ClientHttpRequest request) throws IOException { String auth = GoogleAppsTemplate.this.auth.get(); if (auth != null) { request.getHeaders().set("Authorization", "GoogleLogin auth=" + auth); } requestCallback.doWithRequest(request); } }, responseExtractor); } } public byte[] getUser(final String token, final String name) { return doWithCallback(new ReaderCallback<byte[]>() { @Override public byte[] execute(RestOperations operations) { byte[] result = RestInvoker .prepareGetTo(baseUrl + name) .using(operations) .expecting(byte[].class) .withParam("T", token) .execute(); return result; } }); } public byte[] getAllUser(final String token) { return doWithCallback(new ReaderCallback<byte[]>() { @Override public byte[] execute(RestOperations operations) { byte[] result = RestInvoker .prepareGetTo(baseUrl) .using(operations) .expecting(byte[].class) .withParam("T", token) .execute(); return result; } }); } public byte[] getCustomerId(final String token) { return doWithCallback(new ReaderCallback<byte[]>() { @Override public byte[] execute(RestOperations operations) { byte[] result = RestInvoker .prepareGetTo(customerIdUrl) .using(operations) .expecting(byte[].class) .withParam("T", token) .execute(); return result; } }); } // https://apps-apis.google.com/a/feeds/orgunit/2.0/the customerId/full // organization unit's path // the customerId/the organization user's email public byte[] moveUserToGroup(final String token, final String customerId, final String orgUnit, final GoogleAtom usersToMoveMessage) { return doWithCallback(new ReaderCallback<byte[]>() { @Override public byte[] execute(RestOperations operations) { byte[] result = RestInvoker .preparePutTo(moveToGroupUrl + customerId + "/" + orgUnit) .using(operations) .expecting(byte[].class) .withParam("T", token) .withObject(usersToMoveMessage) .execute(); return result; } }); } }