/** * personium.io * Copyright 2014 FUJITSU LIMITED * * 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 com.fujitsu.dc.core.rs.cell; import java.io.IOException; import java.io.Reader; import java.util.regex.Pattern; import javax.ws.rs.HeaderParam; import javax.ws.rs.POST; import javax.ws.rs.core.Response; import org.apache.wink.webdav.WebDAVMethod.PROPFIND; import org.apache.wink.webdav.WebDAVMethod.PROPPATCH; import org.codehaus.jackson.JsonFactory; import org.codehaus.jackson.JsonParser; import org.codehaus.jackson.JsonToken; import org.codehaus.jackson.map.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fujitsu.dc.common.utils.DcCoreUtils; import com.fujitsu.dc.core.DcCoreException; import com.fujitsu.dc.core.auth.AccessContext; import com.fujitsu.dc.core.auth.CellPrivilege; import com.fujitsu.dc.core.eventbus.DcEventBus; import com.fujitsu.dc.core.eventbus.JSONEvent; import com.fujitsu.dc.core.model.Cell; import com.fujitsu.dc.core.model.DavRsCmp; import com.fujitsu.dc.core.model.ctl.Event; /** * イベントバス用JAX-RS Resource. */ public class EventResource { Cell cell; AccessContext accessContext; DavRsCmp davRsCmp; static Logger log = LoggerFactory.getLogger(EventResource.class); static final int MAXREQUEST_KEY_LENGTH = 128; static final String REQEUST_KEY_DEFAULT_FORMAT = "PCS-%d"; static final Pattern REQUEST_KEY_PATTERN = Pattern.compile("[\\p{Alpha}\\p{Digit}_-]*"); /** * constructor. * @param cell Cell * @param accessContext AccessContext * @param davRsCmp DavRsCmp */ public EventResource(final Cell cell, final AccessContext accessContext, final DavRsCmp davRsCmp) { this.accessContext = accessContext; this.cell = cell; this.davRsCmp = davRsCmp; } /** * イベントの受付. * @param reader リクエストボディ * @param version X-Dc-Versionヘッダー値 * @param requestKey X-Dc-RequestKeyヘッダー値 * @return JAXRS Response */ @POST public final Response receiveEvent(final Reader reader, @HeaderParam(DcCoreUtils.HttpHeaders.X_DC_VERSION) final String version, @HeaderParam(DcCoreUtils.HttpHeaders.X_DC_REQUESTKEY) String requestKey) { // TODO findBugs対策↓ log.debug(this.cell.getName()); log.debug(this.accessContext.getBaseUri()); log.debug(this.davRsCmp.getUrl()); // アクセス制御 this.davRsCmp.checkAccessContext(this.davRsCmp.getAccessContext(), CellPrivilege.EVENT); // X-Dc-RequestKeyの解析(指定なしの場合にデフォルト値を補充) requestKey = validateXDcRequestKey(requestKey); // TODO findBugs対策↓ log.debug(requestKey); // リクエストボディを解析してEventオブジェクトを取得する JSONEvent reqBody = getRequestBody(reader); validateEventProperties(reqBody); // TODO イベントバス系のデータロック // TODO 新規のイベント受付かどうかをESへ検索(current/default.logのデータ検索) // TODO ESへCollectionとLogDavFileを登録/更新(新規:CREATE、更新:uのみPUT) // TODO ログ出力用のデフォルト設定情報を取得 // ログファイル出力 DcEventBus eventBus = new DcEventBus(this.cell); Event event = createEvent(reqBody, requestKey); eventBus.outputEventLog(event); // レスポンス返却 return Response.ok().build(); } /** * ログ設定更新. * @return レスポンス */ @PROPPATCH public final Response updateLogSettings() { // TODO アクセス制御 // this.davRsCmp.checkAccessContext(this.davRsCmp.getAccessContext(), CellPrivilege.LOG); throw DcCoreException.Misc.METHOD_NOT_IMPLEMENTED; } /** * ログ設定取得. * @return レスポンス */ @PROPFIND public final Response getLogSettings() { // TODO アクセス制御 // this.davRsCmp.checkAccessContext(this.davRsCmp.getAccessContext(), CellPrivilege.LOG_READ); throw DcCoreException.Misc.METHOD_NOT_IMPLEMENTED; } /** * リクエストヘッダの X-Dc-RequestKey の正当性チェックを行う. <br/> * 不正な場合には例外を発する. <br/> * 未指定時には、デフォルト値を補充する. * @param requestKey リクエストヘッダ * @return 正当性チェック通過後の X-Dc-RequetKeyの値 */ public static String validateXDcRequestKey(String requestKey) { if (null == requestKey) { requestKey = String.format(REQEUST_KEY_DEFAULT_FORMAT, System.currentTimeMillis()); } if (MAXREQUEST_KEY_LENGTH < requestKey.length()) { throw DcCoreException.Event.X_DC_REQUESTKEY_INVALID; } if (!REQUEST_KEY_PATTERN.matcher(requestKey).matches()) { throw DcCoreException.Event.X_DC_REQUESTKEY_INVALID; } return requestKey; } /** * リクエストボディを解析してEventオブジェクトを取得する. * @param reader Http入力ストリーム * @return 解析したEventオブジェクト */ protected JSONEvent getRequestBody(final Reader reader) { JSONEvent event = null; JsonParser jp = null; ObjectMapper mapper = new ObjectMapper(); JsonFactory f = new JsonFactory(); try { jp = f.createJsonParser(reader); JsonToken token = jp.nextToken(); // JSONルート要素("{") if (token == JsonToken.START_OBJECT) { event = mapper.readValue(jp, JSONEvent.class); } else { throw DcCoreException.Event.JSON_PARSE_ERROR; } } catch (IOException e) { throw DcCoreException.Event.JSON_PARSE_ERROR; } return event; } /** * Event内の各プロパティ値をバリデートする. * @param event Eventオブジェクト */ protected void validateEventProperties(final JSONEvent event) { Event.LEVEL level = event.getLevel(); if (level == null) { throw DcCoreException.Event.INPUT_REQUIRED_FIELD_MISSING.params("level"); } else if (!JSONEvent.validateLevel(level)) { throw DcCoreException.Event.REQUEST_FIELD_FORMAT_ERROR.params("level"); } String action = event.getAction(); if (action == null) { throw DcCoreException.Event.INPUT_REQUIRED_FIELD_MISSING.params("action"); } else if (!JSONEvent.validateAction(action)) { throw DcCoreException.Event.REQUEST_FIELD_FORMAT_ERROR.params("action"); } String object = event.getObject(); if (object == null) { throw DcCoreException.Event.INPUT_REQUIRED_FIELD_MISSING.params("object"); } else if (!JSONEvent.validateObject(object)) { throw DcCoreException.Event.REQUEST_FIELD_FORMAT_ERROR.params("object"); } String result = event.getResult(); if (result == null) { throw DcCoreException.Event.INPUT_REQUIRED_FIELD_MISSING.params("result"); } else if (!JSONEvent.validateResult(result)) { throw DcCoreException.Event.REQUEST_FIELD_FORMAT_ERROR.params("result"); } } /** * 外部イベントログ出力用Eventオブジェクトを生成. * @param reqBody JSON表現リクエストボディ * @param requestKey RequestKeyヘッダの値 * @return Eventオブジェクト */ protected Event createEvent(JSONEvent reqBody, String requestKey) { Event event = new Event(); event.setLevel(reqBody.getLevel()); event.setAction(reqBody.getAction()); event.setObject(reqBody.getObject()); event.setResult(reqBody.getResult()); event.setRequestKey(requestKey); event.setName("client"); event.setSchema(accessContext.getSchema()); event.setSubject(accessContext.getSubject()); return event; } /** * 内部イベントログ出力用Eventオブジェクトを生成. * @param reqBody JSON表現リクエストボディ * @param requestKey RequestKeyヘッダの値 * @param accessContext accessContext * @return Eventオブジェクト */ public static Event createEvent(JSONEvent reqBody, String requestKey, AccessContext accessContext) { Event event = new Event(); event.setLevel(reqBody.getLevel()); event.setAction(reqBody.getAction()); event.setObject(reqBody.getObject()); event.setResult(reqBody.getResult()); event.setRequestKey(requestKey); event.setName("server"); event.setSchema(accessContext.getSchema()); event.setSubject(accessContext.getSubject()); return event; } }