DataAPI.java

/*******************************************************************************
 * Copyright 2017 The MIT Internet Trust Consortium
 *
 * Portions copyright 2011-2013 The MITRE Corporation
 *
 * 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.mitre.openid.connect.web;

import java.io.IOException;
import java.io.Reader;
import java.security.Principal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletResponse;

import org.mitre.openid.connect.config.ConfigurationPropertiesBean;
import org.mitre.openid.connect.service.MITREidDataService;
import org.mitre.openid.connect.service.impl.MITREidDataService_1_3;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.google.common.collect.ImmutableList;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;

/**
 * API endpoint for importing and exporting the current state of a server.
 * Includes all tokens, grants, whitelists, blacklists, and clients.
 *
 * @author jricher
 *
 */
@Controller
@RequestMapping("/" + DataAPI.URL)
@PreAuthorize("hasRole('ROLE_ADMIN')") // you need to be an admin to even think about this -- this is a potentially dangerous API!!
public class DataAPI {

	public static final String URL = RootController.API_URL + "/data";

	/**
	 * Logger for this class
	 */
	private static final Logger logger = LoggerFactory.getLogger(DataAPI.class);

	private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");

	@Autowired
	private ConfigurationPropertiesBean config;

	@Autowired
	private List<MITREidDataService> importers;

	private List<String> supportedVersions = ImmutableList.of(
			MITREidDataService.MITREID_CONNECT_1_0,
			MITREidDataService.MITREID_CONNECT_1_1,
			MITREidDataService.MITREID_CONNECT_1_2,
			MITREidDataService.MITREID_CONNECT_1_3);

	@Autowired
	private MITREidDataService_1_3 exporter;

	@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
	public String importData(Reader in, Model m) throws IOException {

		JsonReader reader = new JsonReader(in);

		reader.beginObject();

		while (reader.hasNext()) {
			JsonToken tok = reader.peek();
			switch (tok) {
				case NAME:
					String name = reader.nextName();

					if (supportedVersions.contains(name)) {
						// we're working with a known data version tag
						for (MITREidDataService dataService : importers) {
							// dispatch to the correct service
							if (dataService.supportsVersion(name)) {
								dataService.importData(reader);
								break;
							}
						}
					} else {
						// consume the next bit silently for now
						logger.debug("Skipping value for " + name); // TODO: write these out?
						reader.skipValue();
					}
					break;
				case END_OBJECT:
					break;
				case END_DOCUMENT:
					break;
			}
		}

		reader.endObject();

		return "httpCodeView";
	}

	@RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
	public void exportData(HttpServletResponse resp, Principal prin) throws IOException {

		resp.setContentType(MediaType.APPLICATION_JSON_VALUE);

		// this writer puts things out onto the wire
		JsonWriter writer = new JsonWriter(resp.getWriter());
		writer.setIndent("  ");

		try {

			writer.beginObject();

			writer.name("exported-at");
			writer.value(dateFormat.format(new Date()));

			writer.name("exported-from");
			writer.value(config.getIssuer());

			writer.name("exported-by");
			writer.value(prin.getName());

			// delegate to the service to do the actual export
			exporter.exportData(writer);

			writer.endObject(); // end root
			writer.close();

		} catch (IOException e) {
			logger.error("Unable to export data", e);
		}
	}

}